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: 88.1% 74 / 0 / 84
Functions: 100.0% 6 / 0 / 6
Branches: 78.4% 134 / 0 / 171

data/SieveRuleGuards.cpp
Line Branch Exec Source
1 #include "SieveRuleGuards.h"
2
3 #include <QRegularExpression>
4
5 namespace Sieve {
6
7 namespace {
8
9 32 void add(QList<GuardIssue> *issues, GuardIssue::Kind kind, const QString &detail, bool blocking) {
10
4/4
✓ Branch 2 → 3 taken 32 times.
✓ Branch 3 → 4 taken 32 times.
✓ Branch 15 → 5 taken 12 times.
✓ Branch 15 → 16 taken 32 times.
44 for (const GuardIssue &issue : *issues)
11
4/6
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 10 taken 10 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 12 times.
12 if (issue.kind == kind && issue.detail == detail)
12 ✗ return;
13 32 issues->append({kind, detail, blocking});
14 32 }
15
16 68 void checkConditions(const Rule &rule, const GuardContext &context, QList<GuardIssue> *issues) {
17
2/2
✓ Branch 68 → 4 taken 77 times.
✓ Branch 68 → 69 taken 68 times.
145 for (const Condition &condition : rule.conditions) {
18
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 15 taken 77 times.
77 if (condition.kind == Condition::Kind::Size) {
19 ✗ if (condition.sizeLimit == 0)
20 ✗ add(issues, GuardIssue::Kind::EmptyValue, QStringLiteral("size"), true);
21 ✗ continue;
22 }
23
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 23 taken 77 times.
77 if (condition.values.isEmpty())
24 ✗ add(issues, GuardIssue::Kind::EmptyValue, condition.headers.join(QLatin1Char(',')), true);
25
2/2
✓ Branch 51 → 25 taken 77 times.
✓ Branch 51 → 52 taken 77 times.
154 for (const QString &value : condition.values) {
26
3/3
✓ Branch 26 → 27 taken 77 times.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 29 → 36 taken 75 times.
77 if (value.trimmed().isEmpty())
27
2/2
✓ Branch 32 → 33 taken 2 times.
✓ Branch 33 → 34 taken 2 times.
2 add(issues, GuardIssue::Kind::EmptyValue, condition.headers.join(QLatin1Char(',')), true);
28
2/2
✓ Branch 38 → 39 taken 2 times.
✓ Branch 38 → 44 taken 75 times.
79 if (condition.matchType == QLatin1String("regex") &&
29
7/10
✓ Branch 40 → 41 taken 2 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 42 → 43 taken 2 times.
✗ Branch 42 → 44 not taken.
✓ Branch 45 → 46 taken 2 times.
✓ Branch 45 → 47 taken 75 times.
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 49 taken 75 times.
✗ Branch 117 → 118 not taken.
✗ Branch 117 → 119 not taken.
79 !QRegularExpression(value).isValid())
30
1/1
✓ Branch 48 → 49 taken 2 times.
2 add(issues, GuardIssue::Kind::InvalidRegex, value, true);
31 }
32
5/8
✓ Branch 52 → 53 taken 77 times.
✗ Branch 52 → 57 not taken.
✓ Branch 53 → 54 taken 76 times.
✓ Branch 53 → 57 taken 1 time.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 76 times.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 66 taken 77 times.
153 if (condition.kind != Condition::Kind::Size && condition.kind != Condition::Kind::Body &&
33 76 condition.headers.isEmpty())
34 ✗ add(issues, GuardIssue::Kind::EmptyValue, QStringLiteral("header"), true);
35 }
36
2/2
✓ Branch 70 → 71 taken 57 times.
✓ Branch 70 → 99 taken 11 times.
68 if (!context.serverExtensions.isEmpty())
37
5/5
✓ Branch 71 → 72 taken 57 times.
✓ Branch 72 → 73 taken 57 times.
✓ Branch 73 → 74 taken 57 times.
✓ Branch 96 → 75 taken 62 times.
✓ Branch 96 → 97 taken 57 times.
119 for (const QString &extension : extensionsFor(rule))
38
2/2
✓ Branch 77 → 78 taken 5 times.
✓ Branch 77 → 87 taken 57 times.
67 if (!context.serverExtensions.contains(extension) &&
39
2/2
✓ Branch 80 → 81 taken 1 time.
✓ Branch 80 → 86 taken 4 times.
5 !(extension == QLatin1String("imap4flags") &&
40
7/8
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 87 taken 1 time.
✓ Branch 88 → 89 taken 1 time.
✓ Branch 88 → 90 taken 61 times.
✓ Branch 90 → 91 taken 1 time.
✓ Branch 90 → 92 taken 61 times.
✓ Branch 92 → 93 taken 4 times.
✓ Branch 92 → 94 taken 58 times.
64 context.serverExtensions.contains(QStringLiteral("imapflags"))))
41
1/1
✓ Branch 93 → 94 taken 4 times.
61 add(issues, GuardIssue::Kind::UnsupportedExtension, extension, true);
42 68 }
43
44 } // namespace
45
46 20 bool isPlausibleAddress(const QString &address) {
47
4/7
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 15 taken 17 times.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
23 static const QRegularExpression pattern(QStringLiteral("^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"));
48
3/3
✓ Branch 16 → 17 taken 20 times.
✓ Branch 17 → 18 taken 20 times.
✓ Branch 18 → 19 taken 20 times.
20 return pattern.match(address.trimmed()).hasMatch();
49 }
50
51 1173 bool rejectConflicts(const Rule &rule) {
52 1173 bool reject = false;
53 1173 bool delivers = false;
54
2/2
✓ Branch 17 → 4 taken 1271 times.
✓ Branch 17 → 18 taken 1173 times.
2444 for (const Action &action : rule.actions) {
55
3/4
✓ Branch 5 → 6 taken 1271 times.
✗ Branch 5 → 7 not taken.
✓ Branch 6 → 7 taken 14 times.
✓ Branch 6 → 8 taken 1257 times.
1271 reject = reject || action.kind == Action::Kind::Reject;
56
5/6
✓ Branch 9 → 10 taken 1178 times.
✓ Branch 9 → 13 taken 93 times.
✓ Branch 10 → 11 taken 42 times.
✓ Branch 10 → 13 taken 1136 times.
✓ Branch 11 → 12 taken 42 times.
✗ Branch 11 → 13 not taken.
1313 delivers = delivers || action.kind == Action::Kind::FileInto || action.kind == Action::Kind::Keep ||
57
2/2
✓ Branch 12 → 13 taken 21 times.
✓ Branch 12 → 14 taken 21 times.
42 action.kind == Action::Kind::Redirect;
58 }
59
4/4
✓ Branch 18 → 19 taken 14 times.
✓ Branch 18 → 21 taken 1159 times.
✓ Branch 19 → 20 taken 8 times.
✓ Branch 19 → 21 taken 6 times.
1173 return reject && delivers;
60 }
61
62 68 QList<GuardIssue> checkRule(const Rule &rule, const GuardContext &context) {
63 68 QList<GuardIssue> issues;
64
5/6
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 6 taken 63 times.
✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 6 not taken.
✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 12 taken 63 times.
68 if (rule.actions.isEmpty() && !rule.stop)
65
1/1
✓ Branch 9 → 10 taken 5 times.
5 add(&issues, GuardIssue::Kind::NoActions, {}, true);
66
1/1
✓ Branch 12 → 13 taken 68 times.
68 checkConditions(rule, context, &issues);
67 68 int redirects = 0;
68
2/2
✓ Branch 79 → 15 taken 85 times.
✓ Branch 79 → 80 taken 68 times.
153 for (const Action &action : rule.actions) {
69
4/6
✓ Branch 16 → 17 taken 20 times.
✓ Branch 16 → 32 taken 45 times.
✓ Branch 16 → 50 taken 8 times.
✓ Branch 16 → 66 taken 12 times.
✗ Branch 16 → 76 not taken.
✗ Branch 16 → 77 not taken.
85 switch (action.kind) {
70 20 case Action::Kind::Redirect: {
71 20 ++redirects;
72
2/2
✓ Branch 17 → 18 taken 20 times.
✓ Branch 18 → 19 taken 20 times.
20 const QString target = action.target.trimmed().toLower();
73
3/3
✓ Branch 20 → 21 taken 20 times.
✓ Branch 21 → 22 taken 2 times.
✓ Branch 21 → 23 taken 18 times.
20 if (!isPlausibleAddress(target)) {
74
1/1
✓ Branch 22 → 29 taken 2 times.
2 add(&issues, GuardIssue::Kind::InvalidAddress, action.target, true);
75
2/2
✓ Branch 24 → 25 taken 3 times.
✓ Branch 24 → 26 taken 15 times.
18 } else if (context.ownAddresses.contains(target)) {
76
1/1
✓ Branch 25 → 29 taken 3 times.
3 add(&issues, GuardIssue::Kind::ForwardToOwnAddress, target, true); // mail loop
77
2/2
✓ Branch 27 → 28 taken 5 times.
✓ Branch 27 → 29 taken 10 times.
15 } else if (!context.confirmedForwardTargets.contains(target)) {
78
1/1
✓ Branch 28 → 29 taken 5 times.
5 add(&issues, GuardIssue::Kind::ForwardNeedsConfirmation, target, false);
79 }
80 20 break;
81 20 }
82 45 case Action::Kind::FileInto:
83
8/11
✓ Branch 32 → 33 taken 45 times.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 34 → 38 taken 43 times.
✓ Branch 36 → 37 taken 1 time.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 39 → 40 taken 45 times.
✗ Branch 39 → 41 not taken.
✓ Branch 41 → 42 taken 1 time.
✓ Branch 41 → 49 taken 44 times.
✗ Branch 116 → 117 not taken.
✗ Branch 116 → 118 not taken.
45 if (action.target.trimmed().isEmpty() && action.specialUse.isEmpty())
84
1/1
✓ Branch 45 → 46 taken 1 time.
1 add(&issues, GuardIssue::Kind::EmptyValue, QStringLiteral("folder"), true);
85 45 break;
86 8 case Action::Kind::Discard:
87 case Action::Kind::Reject:
88
2/2
✓ Branch 50 → 51 taken 2 times.
✓ Branch 50 → 65 taken 6 times.
8 if (!context.destructiveConfirmed)
89
1/1
✓ Branch 58 → 59 taken 2 times.
2 add(&issues, GuardIssue::Kind::DestructiveNeedsConfirmation,
90
3/10
✓ Branch 51 → 52 taken 2 times.
✗ Branch 51 → 55 not taken.
✗ Branch 60 → 61 not taken.
✓ Branch 60 → 62 taken 2 times.
✓ Branch 62 → 63 taken 2 times.
✗ Branch 62 → 64 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
6 action.kind == Action::Kind::Discard ? QStringLiteral("discard") : QStringLiteral("reject"),
91 false);
92 8 break;
93 12 case Action::Kind::AddFlag:
94 case Action::Kind::SetFlag:
95 case Action::Kind::RemoveFlag:
96
1/2
✗ Branch 67 → 68 not taken.
✓ Branch 67 → 75 taken 12 times.
12 if (action.flags.isEmpty())
97 ✗ add(&issues, GuardIssue::Kind::EmptyValue, QStringLiteral("flag"), true);
98 12 break;
99 ✗ case Action::Kind::Keep:
100 ✗ break;
101 }
102 }
103
3/3
✓ Branch 80 → 81 taken 68 times.
✓ Branch 81 → 82 taken 2 times.
✓ Branch 81 → 89 taken 66 times.
68 if (rejectConflicts(rule))
104
1/1
✓ Branch 85 → 86 taken 2 times.
2 add(&issues, GuardIssue::Kind::RejectWithDelivery, QStringLiteral("reject"), true);
105
4/4
✓ Branch 89 → 90 taken 17 times.
✓ Branch 89 → 95 taken 51 times.
✓ Branch 90 → 91 taken 2 times.
✓ Branch 90 → 95 taken 15 times.
68 if (context.maxRedirects >= 0 && redirects > context.maxRedirects)
106
2/2
✓ Branch 91 → 92 taken 2 times.
✓ Branch 92 → 93 taken 2 times.
2 add(&issues, GuardIssue::Kind::TooManyRedirects, QString::number(context.maxRedirects), true);
107
6/6
✓ Branch 96 → 97 taken 3 times.
✓ Branch 96 → 99 taken 65 times.
✓ Branch 97 → 98 taken 2 times.
✓ Branch 97 → 99 taken 1 time.
✓ Branch 100 → 101 taken 2 times.
✓ Branch 100 → 105 taken 66 times.
68 if (rule.conditions.isEmpty() && !context.everyMessageConfirmed)
108
1/1
✓ Branch 102 → 103 taken 2 times.
2 add(&issues, GuardIssue::Kind::EveryMessageNeedsConfirmation, {}, false);
109 68 return issues;
110 ✗ }
111
112 7 bool canSave(const QList<GuardIssue> &issues) { return issues.isEmpty(); }
113
114 } // namespace Sieve
115