data/SieveRuleCodec.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "SieveRuleCodec.h" | ||
| 2 | |||
| 3 | #include "data/SieveMerge.h" | ||
| 4 | |||
| 5 | #include <QLoggingCategory> | ||
| 6 | #include <QRegularExpression> | ||
| 7 | #include <QSet> | ||
| 8 | |||
| 9 |
4/7✓ Branch 2 → 3 taken 13 times.
✓ Branch 2 → 8 taken 3307 times.
✓ Branch 4 → 5 taken 13 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 13 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
|
3320 | Q_LOGGING_CATEGORY(lcSieveCodec, "mailjd.sieve.codec") |
| 10 | |||
| 11 | namespace Sieve { | ||
| 12 | |||
| 13 | 1693 | bool Condition::operator==(const Condition &other) const { | |
| 14 |
3/4✓ Branch 2 → 3 taken 1001 times.
✓ Branch 2 → 4 taken 692 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1001 times.
|
1693 | if (kind != other.kind || negated != other.negated) |
| 15 | 692 | return false; | |
| 16 |
2/2✓ Branch 5 → 6 taken 20 times.
✓ Branch 5 → 11 taken 981 times.
|
1001 | if (kind == Kind::Size) |
| 17 |
2/4✓ Branch 6 → 7 taken 20 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 20 times.
✗ Branch 7 → 9 not taken.
|
20 | return sizeOver == other.sizeOver && sizeLimit == other.sizeLimit; |
| 18 | 1962 | const auto lower = [](const QStringList &list) { | |
| 19 | 1962 | QStringList result; | |
| 20 |
2/2✓ Branch 10 → 4 taken 2026 times.
✓ Branch 10 → 11 taken 1962 times.
|
3988 | for (const QString &value : list) |
| 21 |
2/2✓ Branch 5 → 6 taken 2026 times.
✓ Branch 6 → 7 taken 2026 times.
|
2026 | result.append(value.toLower()); |
| 22 | 1962 | return result; | |
| 23 | ✗ | }; | |
| 24 |
6/12✓ Branch 16 → 17 taken 876 times.
✓ Branch 16 → 24 taken 29 times.
✓ Branch 18 → 19 taken 857 times.
✓ Branch 18 → 24 taken 19 times.
✓ Branch 26 → 27 taken 981 times.
✗ Branch 26 → 28 not taken.
✓ Branch 28 → 29 taken 981 times.
✗ Branch 28 → 30 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
|
2762 | return lower(headers) == lower(other.headers) && addressPart == other.addressPart && |
| 25 |
9/10✓ Branch 11 → 12 taken 981 times.
✓ Branch 12 → 13 taken 981 times.
✓ Branch 13 → 14 taken 981 times.
✓ Branch 14 → 15 taken 905 times.
✓ Branch 14 → 24 taken 76 times.
✓ Branch 20 → 21 taken 857 times.
✗ Branch 20 → 24 not taken.
✓ Branch 21 → 22 taken 857 times.
✓ Branch 22 → 23 taken 660 times.
✓ Branch 22 → 24 taken 197 times.
|
2762 | matchType == other.matchType && comparator == other.comparator && values == other.values; |
| 26 | } | ||
| 27 | |||
| 28 | 707 | bool Action::operator==(const Action &other) const { | |
| 29 |
2/4✓ Branch 4 → 5 taken 707 times.
✗ Branch 4 → 12 not taken.
✓ Branch 6 → 7 taken 707 times.
✗ Branch 6 → 12 not taken.
|
707 | return kind == other.kind && target == other.target && flags == other.flags && |
| 30 |
4/8✓ Branch 2 → 3 taken 707 times.
✗ Branch 2 → 12 not taken.
✓ Branch 7 → 8 taken 707 times.
✗ Branch 7 → 12 not taken.
✓ Branch 8 → 9 taken 707 times.
✗ Branch 8 → 12 not taken.
✓ Branch 10 → 11 taken 707 times.
✗ Branch 10 → 12 not taken.
|
1414 | copy == other.copy && create == other.create && specialUse == other.specialUse; |
| 31 | } | ||
| 32 | |||
| 33 | 341 | bool Rule::operator==(const Rule &other) const { | |
| 34 |
3/4✓ Branch 4 → 5 taken 341 times.
✗ Branch 4 → 14 not taken.
✓ Branch 6 → 7 taken 34 times.
✓ Branch 6 → 8 taken 307 times.
|
1023 | return name == other.name && enabled == other.enabled && |
| 35 |
1/2✓ Branch 7 → 8 taken 34 times.
✗ Branch 7 → 14 not taken.
|
375 | (conditions.size() < 2 || matchAll == other.matchAll) && |
| 36 |
4/8✓ Branch 3 → 4 taken 341 times.
✗ Branch 3 → 14 not taken.
✓ Branch 9 → 10 taken 341 times.
✗ Branch 9 → 14 not taken.
✓ Branch 11 → 12 taken 341 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 341 times.
✗ Branch 12 → 14 not taken.
|
682 | conditions == other.conditions && actions == other.actions && stop == other.stop; |
| 37 | } | ||
| 38 | |||
| 39 | 6 | bool Vacation::operator==(const Vacation &other) const { | |
| 40 |
3/6✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 19 not taken.
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 19 not taken.
✓ Branch 8 → 9 taken 6 times.
✗ Branch 8 → 19 not taken.
|
12 | return enabled == other.enabled && firstDay == other.firstDay && lastDay == other.lastDay && |
| 41 |
2/4✓ Branch 10 → 11 taken 6 times.
✗ Branch 10 → 19 not taken.
✓ Branch 12 → 13 taken 6 times.
✗ Branch 12 → 19 not taken.
|
12 | zone == other.zone && subject == other.subject && message == other.message && |
| 42 |
4/8✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 19 not taken.
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 19 not taken.
✓ Branch 15 → 16 taken 6 times.
✗ Branch 15 → 19 not taken.
✓ Branch 17 → 18 taken 6 times.
✗ Branch 17 → 19 not taken.
|
12 | days == other.days && addresses == other.addresses && from == other.from; |
| 43 | } | ||
| 44 | |||
| 45 | 2546 | QStringList Document::requiredExtensions() const { | |
| 46 | // S106-085: a set keeps this linear; a server script may list ~10^5 | ||
| 47 | // require entries within the 1 MiB script cap. | ||
| 48 | 2546 | QStringList result; | |
| 49 | 2546 | QSet<QString> seen; | |
| 50 |
2/2✓ Branch 18 → 5 taken 108598 times.
✓ Branch 18 → 19 taken 2546 times.
|
111144 | for (const Block &block : blocks) |
| 51 |
2/2✓ Branch 15 → 8 taken 272197 times.
✓ Branch 15 → 16 taken 108598 times.
|
380795 | for (const QString &extension : block.extensions) |
| 52 |
1/2✓ Branch 10 → 11 taken 272197 times.
✗ Branch 10 → 13 not taken.
|
272197 | if (!seen.contains(extension)) { |
| 53 |
1/1✓ Branch 11 → 12 taken 272197 times.
|
272197 | seen.insert(extension); |
| 54 |
1/1✓ Branch 12 → 13 taken 272197 times.
|
272197 | result.append(extension); |
| 55 | } | ||
| 56 | 2546 | return result; | |
| 57 | 2546 | } | |
| 58 | |||
| 59 | 138 | int Document::ruleCount() const { | |
| 60 | 138 | int count = 0; | |
| 61 |
2/2✓ Branch 10 → 4 taken 4916 times.
✓ Branch 10 → 11 taken 138 times.
|
5054 | for (const Block &block : blocks) |
| 62 |
4/4✓ Branch 5 → 6 taken 377 times.
✓ Branch 5 → 7 taken 4539 times.
✓ Branch 6 → 7 taken 110 times.
✓ Branch 6 → 8 taken 267 times.
|
4916 | if (block.kind == BlockKind::Rule || block.kind == BlockKind::Advanced) |
| 63 | 4649 | ++count; | |
| 64 | 138 | return count; | |
| 65 | } | ||
| 66 | |||
| 67 | 70 | int Document::enabledRuleCount() const { | |
| 68 | 70 | int count = 0; | |
| 69 |
2/2✓ Branch 11 → 4 taken 686 times.
✓ Branch 11 → 12 taken 70 times.
|
756 | for (const Block &block : blocks) |
| 70 |
6/6✓ Branch 5 → 6 taken 482 times.
✓ Branch 5 → 7 taken 204 times.
✓ Branch 6 → 7 taken 76 times.
✓ Branch 6 → 8 taken 406 times.
✓ Branch 7 → 8 taken 62 times.
✓ Branch 7 → 9 taken 218 times.
|
686 | if ((block.kind == BlockKind::Rule && block.rule.enabled) || block.kind == BlockKind::Advanced) |
| 71 | 468 | ++count; | |
| 72 | 70 | return count; | |
| 73 | } | ||
| 74 | |||
| 75 | namespace { | ||
| 76 | |||
| 77 | // --- comment scanning ------------------------------------------------------- | ||
| 78 | |||
| 79 | // Position after the "." line that ends the multi-line string whose body | ||
| 80 | // starts at `bodyStart`; -1 when it is unterminated. S106-093: a dot-stuffed | ||
| 81 | // body line ("..x") does not end the string. | ||
| 82 | 59 | int multiLineEnd(const QByteArray &bytes, int bodyStart) { | |
| 83 | 59 | int lineStart = bodyStart; | |
| 84 |
1/2✓ Branch 27 → 3 taken 179 times.
✗ Branch 27 → 28 not taken.
|
179 | while (lineStart < bytes.size()) { |
| 85 | 179 | int lineEnd = bytes.indexOf('\n', lineStart); | |
| 86 |
3/3✓ Branch 4 → 5 taken 178 times.
✓ Branch 4 → 6 taken 1 time.
✓ Branch 7 → 8 taken 179 times.
|
179 | QByteArray line = bytes.mid(lineStart, lineEnd < 0 ? -1 : lineEnd - lineStart); |
| 87 |
3/3✓ Branch 8 → 9 taken 179 times.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 9 → 11 taken 175 times.
|
179 | if (line.endsWith('\r')) |
| 88 |
1/1✓ Branch 10 → 11 taken 4 times.
|
4 | line.chop(1); |
| 89 |
2/2✓ Branch 12 → 13 taken 59 times.
✓ Branch 12 → 18 taken 120 times.
|
179 | if (line == ".") |
| 90 |
2/2✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 16 taken 58 times.
|
59 | return lineEnd < 0 ? bytes.size() : lineEnd + 1; |
| 91 |
1/2✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 120 times.
|
120 | if (lineEnd < 0) |
| 92 | ✗ | return -1; | |
| 93 | 120 | lineStart = lineEnd + 1; | |
| 94 | 179 | } | |
| 95 | ✗ | return -1; | |
| 96 | } | ||
| 97 | |||
| 98 | // True when `bytes` contains a hash or bracket comment outside strings. | ||
| 99 | 73765 | bool hasComment(const QByteArray &bytes) { | |
| 100 | 73765 | int pos = 0; | |
| 101 |
2/2✓ Branch 70 → 3 taken 3691630 times.
✓ Branch 70 → 71 taken 73749 times.
|
3765379 | while (pos < bytes.size()) { |
| 102 | 3691630 | const char c = bytes.at(pos); | |
| 103 |
2/2✓ Branch 4 → 5 taken 240242 times.
✓ Branch 4 → 19 taken 3451388 times.
|
3691630 | if (c == '"') { |
| 104 | 240242 | ++pos; | |
| 105 |
5/6✓ Branch 12 → 13 taken 2553555 times.
✗ Branch 12 → 16 not taken.
✓ Branch 14 → 15 taken 2313313 times.
✓ Branch 14 → 16 taken 240242 times.
✓ Branch 17 → 6 taken 2313313 times.
✓ Branch 17 → 18 taken 240242 times.
|
2553555 | while (pos < bytes.size() && bytes.at(pos) != '"') |
| 106 |
2/2✓ Branch 7 → 8 taken 5049 times.
✓ Branch 7 → 9 taken 2308264 times.
|
2313313 | pos += bytes.at(pos) == '\\' ? 2 : 1; |
| 107 | 240242 | ++pos; | |
| 108 |
8/10✓ Branch 19 → 20 taken 3451382 times.
✓ Branch 19 → 25 taken 6 times.
✓ Branch 20 → 21 taken 9 times.
✓ Branch 20 → 26 taken 3451373 times.
✓ Branch 22 → 23 taken 9 times.
✗ Branch 22 → 26 not taken.
✓ Branch 24 → 25 taken 9 times.
✗ Branch 24 → 26 not taken.
✓ Branch 27 → 28 taken 15 times.
✓ Branch 27 → 29 taken 3451373 times.
|
3451388 | } else if (c == '#' || (c == '/' && pos + 1 < bytes.size() && bytes.at(pos + 1) == '*')) { |
| 109 | 15 | return true; | |
| 110 |
13/18✓ Branch 29 → 30 taken 3247732 times.
✓ Branch 29 → 31 taken 203641 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 36 taken 3247732 times.
✓ Branch 31 → 32 taken 203641 times.
✓ Branch 32 → 33 taken 203641 times.
✓ Branch 34 → 35 taken 60 times.
✓ Branch 34 → 36 taken 203581 times.
✓ Branch 37 → 38 taken 203641 times.
✓ Branch 37 → 39 taken 3247732 times.
✓ Branch 39 → 40 taken 203641 times.
✓ Branch 39 → 41 taken 3247732 times.
✓ Branch 41 → 42 taken 60 times.
✓ Branch 41 → 68 taken 3451313 times.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
✗ Branch 77 → 78 not taken.
✗ Branch 77 → 79 not taken.
|
3451373 | } else if ((c == 't' || c == 'T') && bytes.mid(pos, 5).toLower() == "text:") { |
| 111 | // A hash comment may follow "text:" on its line (RFC 5228 §8.1). | ||
| 112 | 60 | int after = pos + 5; | |
| 113 |
6/8✓ Branch 45 → 46 taken 61 times.
✗ Branch 45 → 51 not taken.
✓ Branch 47 → 48 taken 60 times.
✓ Branch 47 → 50 taken 1 time.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 60 times.
✓ Branch 52 → 43 taken 1 time.
✓ Branch 52 → 53 taken 60 times.
|
61 | while (after < bytes.size() && (bytes.at(after) == ' ' || bytes.at(after) == '\t')) |
| 114 | 1 | ++after; | |
| 115 |
5/6✓ Branch 54 → 55 taken 60 times.
✗ Branch 54 → 58 not taken.
✓ Branch 56 → 57 taken 1 time.
✓ Branch 56 → 58 taken 59 times.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 61 taken 59 times.
|
60 | if (after < bytes.size() && bytes.at(after) == '#') |
| 116 | 1 | return true; | |
| 117 | 59 | const int bodyStart = bytes.indexOf('\n', after); | |
| 118 |
1/2✗ Branch 62 → 63 not taken.
✓ Branch 62 → 64 taken 59 times.
|
59 | if (bodyStart < 0) |
| 119 | ✗ | return false; | |
| 120 | 59 | const int end = multiLineEnd(bytes, bodyStart + 1); | |
| 121 |
1/2✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 59 times.
|
59 | if (end < 0) |
| 122 | ✗ | return false; | |
| 123 | 59 | pos = end; | |
| 124 | } else { | ||
| 125 | 3451313 | ++pos; | |
| 126 | } | ||
| 127 | } | ||
| 128 | 73749 | return false; | |
| 129 | } | ||
| 130 | |||
| 131 | // --- test decoding ------------------------------------------------------------ | ||
| 132 | |||
| 133 | 76541 | bool isMatchType(const QString &tag) { | |
| 134 |
4/4✓ Branch 7 → 8 taken 439 times.
✓ Branch 7 → 14 taken 63866 times.
✓ Branch 10 → 11 taken 341 times.
✓ Branch 10 → 14 taken 98 times.
|
141285 | return tag == QLatin1String(":is") || tag == QLatin1String(":contains") || |
| 135 |
4/4✓ Branch 4 → 5 taken 64305 times.
✓ Branch 4 → 14 taken 12236 times.
✓ Branch 13 → 14 taken 65 times.
✓ Branch 13 → 15 taken 276 times.
|
141285 | tag == QLatin1String(":matches") || tag == QLatin1String(":regex"); |
| 136 | } | ||
| 137 | |||
| 138 | 170 | bool isAddressPart(const QString &tag) { | |
| 139 |
5/6✓ Branch 4 → 5 taken 154 times.
✓ Branch 4 → 11 taken 16 times.
✓ Branch 7 → 8 taken 149 times.
✓ Branch 7 → 11 taken 5 times.
✓ Branch 10 → 11 taken 149 times.
✗ Branch 10 → 12 not taken.
|
319 | return tag == QLatin1String(":all") || tag == QLatin1String(":localpart") || |
| 140 | 319 | tag == QLatin1String(":domain"); | |
| 141 | } | ||
| 142 | |||
| 143 | 76351 | bool decodeComparisonArguments(const Test &test, Condition *condition, | |
| 144 | QList<QStringList> *positional) { | ||
| 145 | 76351 | bool haveMatch = false; | |
| 146 | 76351 | bool havePart = false; | |
| 147 | 76351 | const QList<Argument> &args = test.arguments; | |
| 148 |
2/2✓ Branch 67 → 3 taken 229098 times.
✓ Branch 67 → 68 taken 76265 times.
|
305363 | for (int i = 0; i < args.size(); ++i) { |
| 149 | 229098 | const Argument &arg = args.at(i); | |
| 150 |
2/2✓ Branch 4 → 5 taken 152495 times.
✓ Branch 4 → 9 taken 76603 times.
|
229098 | if (arg.kind == Argument::Kind::StringList) { |
| 151 |
2/2✓ Branch 5 → 6 taken 152495 times.
✓ Branch 6 → 7 taken 152495 times.
|
152495 | positional->append(arg.values()); |
| 152 | 152495 | continue; | |
| 153 | } | ||
| 154 |
3/6✓ Branch 9 → 10 taken 76603 times.
✗ Branch 9 → 12 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 76603 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 76603 times.
|
76603 | if (arg.kind != Argument::Kind::Tag || !positional->isEmpty()) |
| 155 | ✗ | return false; | |
| 156 |
2/2✓ Branch 18 → 19 taken 62 times.
✓ Branch 18 → 35 taken 76541 times.
|
76603 | if (arg.text == QLatin1String(":comparator")) { |
| 157 |
1/2✓ Branch 22 → 23 taken 62 times.
✗ Branch 22 → 28 not taken.
|
124 | if (!condition->comparator.isEmpty() || i + 1 >= args.size() || |
| 158 |
4/8✓ Branch 20 → 21 taken 62 times.
✗ Branch 20 → 28 not taken.
✓ Branch 24 → 25 taken 62 times.
✗ Branch 24 → 28 not taken.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 62 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 62 times.
|
124 | args.at(i + 1).kind != Argument::Kind::StringList || args.at(i + 1).strings.size() != 1) |
| 159 | ✗ | return false; | |
| 160 | 62 | condition->comparator = args.at(++i).strings.first().value; | |
| 161 |
5/6✓ Branch 36 → 37 taken 76265 times.
✓ Branch 36 → 39 taken 276 times.
✓ Branch 37 → 38 taken 76265 times.
✗ Branch 37 → 39 not taken.
✓ Branch 40 → 41 taken 76265 times.
✓ Branch 40 → 45 taken 276 times.
|
76541 | } else if (isMatchType(arg.text) && !haveMatch) { |
| 162 | 76265 | haveMatch = true; | |
| 163 |
1/1✓ Branch 41 → 42 taken 76265 times.
|
76265 | condition->matchType = arg.text.mid(1); |
| 164 |
6/8✓ Branch 45 → 46 taken 170 times.
✓ Branch 45 → 50 taken 106 times.
✓ Branch 47 → 48 taken 170 times.
✗ Branch 47 → 50 not taken.
✓ Branch 48 → 49 taken 170 times.
✗ Branch 48 → 50 not taken.
✓ Branch 51 → 52 taken 170 times.
✓ Branch 51 → 56 taken 106 times.
|
276 | } else if (condition->kind == Condition::Kind::Address && isAddressPart(arg.text) && !havePart) { |
| 165 | 170 | havePart = true; | |
| 166 |
1/1✓ Branch 52 → 53 taken 170 times.
|
170 | condition->addressPart = arg.text.mid(1); |
| 167 |
5/6✓ Branch 56 → 57 taken 20 times.
✓ Branch 56 → 61 taken 86 times.
✓ Branch 59 → 60 taken 20 times.
✗ Branch 59 → 61 not taken.
✓ Branch 62 → 63 taken 20 times.
✓ Branch 62 → 64 taken 86 times.
|
106 | } else if (condition->kind == Condition::Kind::Body && arg.text == QLatin1String(":text")) { |
| 168 | 20 | continue; | |
| 169 | } else { | ||
| 170 | 86 | return false; | |
| 171 | } | ||
| 172 | } | ||
| 173 | 76265 | return true; | |
| 174 | } | ||
| 175 | |||
| 176 | 76720 | bool decodeCondition(const Test &input, Condition *condition) { | |
| 177 | 76720 | const Test *test = &input; | |
| 178 |
2/2✓ Branch 4 → 5 taken 125 times.
✓ Branch 4 → 19 taken 76595 times.
|
76720 | if (test->name == QLatin1String("not")) { |
| 179 |
4/8✓ Branch 6 → 7 taken 125 times.
✗ Branch 6 → 10 not taken.
✓ Branch 7 → 8 taken 125 times.
✗ Branch 7 → 10 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 125 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 125 times.
|
125 | if (!test->arguments.isEmpty() || test->testList || test->tests.size() != 1) |
| 180 | ✗ | return false; | |
| 181 | 125 | condition->negated = true; | |
| 182 | 125 | test = &test->tests.first(); | |
| 183 |
2/2✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 19 taken 124 times.
|
125 | if (test->name == QLatin1String("not")) |
| 184 | 1 | return false; | |
| 185 | } | ||
| 186 |
2/2✓ Branch 20 → 21 taken 49 times.
✓ Branch 20 → 22 taken 76670 times.
|
76719 | if (!test->tests.isEmpty()) |
| 187 | 49 | return false; | |
| 188 |
2/2✓ Branch 24 → 25 taken 242 times.
✓ Branch 24 → 53 taken 76428 times.
|
76670 | if (test->name == QLatin1String("size")) { |
| 189 | 242 | condition->kind = Condition::Kind::Size; | |
| 190 |
3/6✓ Branch 26 → 27 taken 242 times.
✗ Branch 26 → 31 not taken.
✓ Branch 28 → 29 taken 242 times.
✗ Branch 28 → 31 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 242 times.
|
484 | if (test->arguments.size() != 2 || test->arguments.at(0).kind != Argument::Kind::Tag || |
| 191 |
1/2✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 242 times.
|
242 | test->arguments.at(1).kind != Argument::Kind::Number) |
| 192 | ✗ | return false; | |
| 193 | 242 | const QString tag = test->arguments.at(0).text; | |
| 194 |
4/6✓ Branch 39 → 40 taken 60 times.
✓ Branch 39 → 44 taken 182 times.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 60 times.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 242 times.
|
242 | if (tag != QLatin1String(":over") && tag != QLatin1String(":under")) |
| 195 | ✗ | return false; | |
| 196 | 242 | condition->sizeOver = tag == QLatin1String(":over"); | |
| 197 | 242 | condition->sizeLimit = test->arguments.at(1).number; | |
| 198 | 242 | return true; | |
| 199 | 242 | } | |
| 200 |
2/2✓ Branch 55 → 56 taken 13314 times.
✓ Branch 55 → 57 taken 63114 times.
|
76428 | if (test->name == QLatin1String("address")) |
| 201 | 13314 | condition->kind = Condition::Kind::Address; | |
| 202 |
2/2✓ Branch 59 → 60 taken 63002 times.
✓ Branch 59 → 61 taken 112 times.
|
63114 | else if (test->name == QLatin1String("header")) |
| 203 | 63002 | condition->kind = Condition::Kind::Header; | |
| 204 |
2/2✓ Branch 63 → 64 taken 35 times.
✓ Branch 63 → 65 taken 77 times.
|
112 | else if (test->name == QLatin1String("body")) |
| 205 | 35 | condition->kind = Condition::Kind::Body; | |
| 206 | else | ||
| 207 | 77 | return false; | |
| 208 | 76351 | QList<QStringList> positional; | |
| 209 |
3/3✓ Branch 66 → 67 taken 76351 times.
✓ Branch 67 → 68 taken 86 times.
✓ Branch 67 → 69 taken 76265 times.
|
76351 | if (!decodeComparisonArguments(*test, condition, &positional)) |
| 210 | 86 | return false; | |
| 211 |
2/2✓ Branch 69 → 70 taken 35 times.
✓ Branch 69 → 77 taken 76230 times.
|
76265 | if (condition->kind == Condition::Kind::Body) { |
| 212 |
1/2✗ Branch 71 → 72 not taken.
✓ Branch 71 → 73 taken 35 times.
|
35 | if (positional.size() != 1) |
| 213 | ✗ | return false; | |
| 214 | 35 | condition->values = positional.at(0); | |
| 215 | 35 | return !condition->values.isEmpty(); | |
| 216 | } | ||
| 217 |
1/2✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 76230 times.
|
76230 | if (positional.size() != 2) |
| 218 | ✗ | return false; | |
| 219 | 76230 | condition->headers = positional.at(0); | |
| 220 | 76230 | condition->values = positional.at(1); | |
| 221 |
2/4✓ Branch 85 → 86 taken 76230 times.
✗ Branch 85 → 89 not taken.
✓ Branch 87 → 88 taken 76230 times.
✗ Branch 87 → 89 not taken.
|
76230 | return !condition->headers.isEmpty() && !condition->values.isEmpty(); |
| 222 | 76351 | } | |
| 223 | |||
| 224 | 72366 | bool decodeTest(const Test &test, Rule *rule) { | |
| 225 |
6/8✓ Branch 4 → 5 taken 119 times.
✓ Branch 4 → 10 taken 72247 times.
✓ Branch 6 → 7 taken 119 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 119 times.
✗ Branch 8 → 10 not taken.
✓ Branch 11 → 12 taken 119 times.
✓ Branch 11 → 13 taken 72247 times.
|
72366 | if (test.name == QLatin1String("true") && test.arguments.isEmpty() && test.tests.isEmpty()) |
| 226 | 119 | return true; | |
| 227 |
6/6✓ Branch 15 → 16 taken 55002 times.
✓ Branch 15 → 19 taken 17245 times.
✓ Branch 18 → 19 taken 133 times.
✓ Branch 18 → 20 taken 54869 times.
✓ Branch 21 → 22 taken 17378 times.
✓ Branch 21 → 51 taken 54869 times.
|
72247 | if (test.name == QLatin1String("allof") || test.name == QLatin1String("anyof")) { |
| 228 |
4/8✓ Branch 23 → 24 taken 17378 times.
✗ Branch 23 → 27 not taken.
✓ Branch 24 → 25 taken 17378 times.
✗ Branch 24 → 27 not taken.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 17378 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 17378 times.
|
17378 | if (!test.arguments.isEmpty() || !test.testList || test.tests.isEmpty()) |
| 229 | ✗ | return false; | |
| 230 | 17378 | rule->matchAll = test.name == QLatin1String("allof"); | |
| 231 | 17378 | rule->explicitList = test.tests.size() == 1; | |
| 232 |
2/2✓ Branch 49 → 36 taken 21851 times.
✓ Branch 49 → 50 taken 17306 times.
|
39157 | for (const Test &operand : test.tests) { |
| 233 |
1/1✓ Branch 37 → 38 taken 21851 times.
|
21851 | Condition condition; |
| 234 |
3/3✓ Branch 38 → 39 taken 21851 times.
✓ Branch 39 → 40 taken 72 times.
✓ Branch 39 → 41 taken 21779 times.
|
21851 | if (!decodeCondition(operand, &condition)) |
| 235 | 72 | return false; | |
| 236 |
1/1✓ Branch 41 → 42 taken 21779 times.
|
21779 | rule->conditions.append(condition); |
| 237 | 21851 | } | |
| 238 | 17306 | return true; | |
| 239 | } | ||
| 240 |
1/1✓ Branch 51 → 52 taken 54869 times.
|
54869 | Condition condition; |
| 241 |
3/3✓ Branch 52 → 53 taken 54869 times.
✓ Branch 53 → 54 taken 141 times.
✓ Branch 53 → 55 taken 54728 times.
|
54869 | if (!decodeCondition(test, &condition)) |
| 242 | 141 | return false; | |
| 243 |
1/1✓ Branch 55 → 56 taken 54728 times.
|
54728 | rule->conditions.append(condition); |
| 244 | 54728 | return true; | |
| 245 | 54869 | } | |
| 246 | |||
| 247 | // --- action decoding ---------------------------------------------------------- | ||
| 248 | |||
| 249 | 72784 | bool singleString(const Argument &arg, QString *value) { | |
| 250 |
3/6✓ Branch 2 → 3 taken 72784 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 72784 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 72784 times.
|
72784 | if (arg.kind != Argument::Kind::StringList || arg.strings.size() != 1) |
| 251 | ✗ | return false; | |
| 252 | 72784 | *value = arg.strings.first().value; | |
| 253 | 72784 | return true; | |
| 254 | } | ||
| 255 | |||
| 256 | 70726 | bool decodeFileInto(const Command &command, Action *action) { | |
| 257 | 70726 | action->kind = Action::Kind::FileInto; | |
| 258 | 70726 | const QList<Argument> &args = command.arguments; | |
| 259 |
1/2✓ Branch 44 → 3 taken 70829 times.
✗ Branch 44 → 45 not taken.
|
70829 | for (int i = 0; i < args.size(); ++i) { |
| 260 | 70829 | const Argument &arg = args.at(i); | |
| 261 |
2/2✓ Branch 4 → 5 taken 70726 times.
✓ Branch 4 → 9 taken 103 times.
|
70829 | if (arg.kind == Argument::Kind::StringList) { |
| 262 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 70726 times.
|
70726 | if (i != args.size() - 1) |
| 263 | ✗ | return false; | |
| 264 | 70726 | return singleString(arg, &action->target); | |
| 265 | } | ||
| 266 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 103 times.
|
103 | if (arg.kind != Argument::Kind::Tag) |
| 267 | ✗ | return false; | |
| 268 |
5/6✓ Branch 13 → 14 taken 53 times.
✓ Branch 13 → 16 taken 50 times.
✓ Branch 14 → 15 taken 53 times.
✗ Branch 14 → 16 not taken.
✓ Branch 17 → 18 taken 53 times.
✓ Branch 17 → 19 taken 50 times.
|
103 | if (arg.text == QLatin1String(":copy") && !action->copy) |
| 269 | 53 | action->copy = true; | |
| 270 |
5/6✓ Branch 21 → 22 taken 30 times.
✓ Branch 21 → 24 taken 20 times.
✓ Branch 22 → 23 taken 30 times.
✗ Branch 22 → 24 not taken.
✓ Branch 25 → 26 taken 30 times.
✓ Branch 25 → 27 taken 20 times.
|
50 | else if (arg.text == QLatin1String(":create") && !action->create) |
| 271 | 30 | action->create = true; | |
| 272 |
2/4✓ Branch 31 → 32 taken 20 times.
✗ Branch 31 → 38 not taken.
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 38 not taken.
|
60 | else if (arg.text == QLatin1String(":specialuse") && action->specialUse.isEmpty() && |
| 273 |
3/6✓ Branch 29 → 30 taken 20 times.
✗ Branch 29 → 38 not taken.
✓ Branch 36 → 37 taken 20 times.
✗ Branch 36 → 38 not taken.
✓ Branch 39 → 40 taken 20 times.
✗ Branch 39 → 41 not taken.
|
60 | i + 1 < args.size() && singleString(args.at(i + 1), &action->specialUse)) |
| 274 | 20 | ++i; | |
| 275 | else | ||
| 276 | ✗ | return false; | |
| 277 | } | ||
| 278 | ✗ | return false; | |
| 279 | } | ||
| 280 | |||
| 281 | 77311 | bool decodeAction(const Command &command, Action *action) { | |
| 282 |
5/6✓ Branch 2 → 3 taken 77304 times.
✓ Branch 2 → 5 taken 7 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 77304 times.
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 9 taken 77304 times.
|
77311 | if (command.hasBlock || !command.tests.isEmpty()) |
| 283 | 7 | return false; | |
| 284 | 77304 | const QString &name = command.name; | |
| 285 | 77304 | const QList<Argument> &args = command.arguments; | |
| 286 |
2/2✓ Branch 11 → 12 taken 70726 times.
✓ Branch 11 → 13 taken 6578 times.
|
77304 | if (name == QLatin1String("fileinto")) |
| 287 | 70726 | return decodeFileInto(command, action); | |
| 288 |
2/2✓ Branch 15 → 16 taken 489 times.
✓ Branch 15 → 37 taken 6089 times.
|
6578 | if (name == QLatin1String("redirect")) { |
| 289 | 489 | action->kind = Action::Kind::Redirect; | |
| 290 |
4/6✓ Branch 17 → 18 taken 486 times.
✓ Branch 17 → 25 taken 3 times.
✓ Branch 19 → 20 taken 486 times.
✗ Branch 19 → 25 not taken.
✓ Branch 23 → 24 taken 486 times.
✗ Branch 23 → 25 not taken.
|
975 | if (args.size() == 2 && args.at(0).kind == Argument::Kind::Tag && |
| 291 |
2/2✓ Branch 26 → 27 taken 486 times.
✓ Branch 26 → 29 taken 3 times.
|
975 | args.at(0).text == QLatin1String(":copy")) { |
| 292 | 486 | action->copy = true; | |
| 293 | 486 | return singleString(args.at(1), &action->target); | |
| 294 | } | ||
| 295 |
2/4✓ Branch 30 → 31 taken 3 times.
✗ Branch 30 → 35 not taken.
✓ Branch 33 → 34 taken 3 times.
✗ Branch 33 → 35 not taken.
|
3 | return args.size() == 1 && singleString(args.at(0), &action->target); |
| 296 | } | ||
| 297 |
6/6✓ Branch 39 → 40 taken 3544 times.
✓ Branch 39 → 46 taken 2545 times.
✓ Branch 42 → 43 taken 1721 times.
✓ Branch 42 → 46 taken 1823 times.
✓ Branch 45 → 46 taken 476 times.
✓ Branch 45 → 47 taken 1245 times.
|
7810 | if (name == QLatin1String("addflag") || name == QLatin1String("setflag") || |
| 298 |
2/2✓ Branch 48 → 49 taken 4844 times.
✓ Branch 48 → 72 taken 1245 times.
|
7810 | name == QLatin1String("removeflag")) { |
| 299 |
2/2✓ Branch 51 → 52 taken 2545 times.
✓ Branch 51 → 53 taken 2299 times.
|
7143 | action->kind = name == QLatin1String("addflag") ? Action::Kind::AddFlag |
| 300 |
2/2✓ Branch 55 → 56 taken 1823 times.
✓ Branch 55 → 57 taken 476 times.
|
2299 | : name == QLatin1String("setflag") ? Action::Kind::SetFlag |
| 301 | : Action::Kind::RemoveFlag; | ||
| 302 |
5/6✓ Branch 60 → 61 taken 4843 times.
✓ Branch 60 → 63 taken 1 time.
✗ Branch 62 → 63 not taken.
✓ Branch 62 → 64 taken 4843 times.
✓ Branch 65 → 66 taken 1 time.
✓ Branch 65 → 67 taken 4843 times.
|
4844 | if (args.size() != 1 || args.at(0).kind != Argument::Kind::StringList) |
| 303 | 1 | return false; | |
| 304 |
1/1✓ Branch 68 → 69 taken 4843 times.
|
4843 | action->flags = args.at(0).values(); |
| 305 | 4843 | return true; | |
| 306 | } | ||
| 307 |
6/6✓ Branch 74 → 75 taken 1003 times.
✓ Branch 74 → 78 taken 242 times.
✓ Branch 77 → 78 taken 61 times.
✓ Branch 77 → 79 taken 942 times.
✓ Branch 80 → 81 taken 303 times.
✓ Branch 80 → 87 taken 942 times.
|
1245 | if (name == QLatin1String("keep") || name == QLatin1String("discard")) { |
| 308 |
2/2✓ Branch 83 → 84 taken 242 times.
✓ Branch 83 → 85 taken 61 times.
|
303 | action->kind = name == QLatin1String("keep") ? Action::Kind::Keep : Action::Kind::Discard; |
| 309 | 303 | return args.isEmpty(); | |
| 310 | } | ||
| 311 |
2/2✓ Branch 89 → 90 taken 929 times.
✓ Branch 89 → 98 taken 13 times.
|
942 | if (name == QLatin1String("reject")) { |
| 312 | 929 | action->kind = Action::Kind::Reject; | |
| 313 |
2/4✓ Branch 91 → 92 taken 929 times.
✗ Branch 91 → 96 not taken.
✓ Branch 94 → 95 taken 929 times.
✗ Branch 94 → 96 not taken.
|
929 | return args.size() == 1 && singleString(args.at(0), &action->target); |
| 314 | } | ||
| 315 | 13 | return false; | |
| 316 | } | ||
| 317 | |||
| 318 | 72153 | bool decodeActions(const QList<Command> &commands, Rule *rule) { | |
| 319 |
2/2✓ Branch 31 → 3 taken 141233 times.
✓ Branch 31 → 32 taken 72131 times.
|
213364 | for (int i = 0; i < commands.size(); ++i) { |
| 320 | 141233 | const Command &command = commands.at(i); | |
| 321 |
2/2✓ Branch 6 → 7 taken 63922 times.
✓ Branch 6 → 19 taken 77311 times.
|
141233 | if (command.name == QLatin1String("stop")) { |
| 322 |
6/8✓ Branch 8 → 9 taken 63921 times.
✓ Branch 8 → 14 taken 1 time.
✓ Branch 10 → 11 taken 63921 times.
✗ Branch 10 → 14 not taken.
✓ Branch 12 → 13 taken 63921 times.
✗ Branch 12 → 14 not taken.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 63921 times.
|
127843 | if (i != commands.size() - 1 || !command.arguments.isEmpty() || !command.tests.isEmpty() || |
| 323 |
1/2✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 63921 times.
|
63921 | command.hasBlock) |
| 324 | 22 | return false; | |
| 325 | 63921 | rule->stop = true; | |
| 326 | 63921 | continue; | |
| 327 | } | ||
| 328 | 77311 | Action action; | |
| 329 |
3/3✓ Branch 19 → 20 taken 77311 times.
✓ Branch 20 → 21 taken 21 times.
✓ Branch 20 → 22 taken 77290 times.
|
77311 | if (!decodeAction(command, &action)) |
| 330 | 21 | return false; | |
| 331 |
1/1✓ Branch 22 → 23 taken 77290 times.
|
77290 | rule->actions.append(action); |
| 332 | 77311 | } | |
| 333 |
3/4✓ Branch 33 → 34 taken 16 times.
✓ Branch 33 → 35 taken 72115 times.
✓ Branch 34 → 35 taken 16 times.
✗ Branch 34 → 36 not taken.
|
72131 | return !rule->actions.isEmpty() || rule->stop; |
| 334 | } | ||
| 335 | |||
| 336 | // --- vacation decoding -------------------------------------------------------- | ||
| 337 | |||
| 338 | 71 | bool decodeDateTest(const Test &test, Vacation *vacation) { | |
| 339 |
3/6✓ Branch 4 → 5 taken 71 times.
✗ Branch 4 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 71 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 71 times.
|
71 | if (test.name != QLatin1String("currentdate") || !test.tests.isEmpty()) |
| 340 | ✗ | return false; | |
| 341 | 71 | QString zone; | |
| 342 | 71 | QString relation; | |
| 343 | 71 | QStringList positional; | |
| 344 | 71 | const QList<Argument> &args = test.arguments; | |
| 345 |
2/2✓ Branch 50 → 12 taken 264 times.
✓ Branch 50 → 51 taken 71 times.
|
335 | for (int i = 0; i < args.size(); ++i) { |
| 346 | 264 | const Argument &arg = args.at(i); | |
| 347 |
2/2✓ Branch 13 → 14 taken 142 times.
✓ Branch 13 → 22 taken 122 times.
|
264 | if (arg.kind == Argument::Kind::StringList) { |
| 348 |
3/3✓ Branch 14 → 15 taken 142 times.
✓ Branch 17 → 18 taken 142 times.
✓ Branch 18 → 19 taken 142 times.
|
142 | positional.append(arg.values().join(QLatin1Char('\x1f'))); |
| 349 | 142 | continue; | |
| 350 | } | ||
| 351 |
3/6✓ Branch 22 → 23 taken 122 times.
✗ Branch 22 → 25 not taken.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 122 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 122 times.
|
122 | if (arg.kind != Argument::Kind::Tag || i + 1 >= args.size()) |
| 352 | ✗ | return false; | |
| 353 | 122 | QString value; | |
| 354 |
1/2✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 122 times.
|
122 | if (!singleString(args.at(i + 1), &value)) |
| 355 | ✗ | return false; | |
| 356 |
2/2✓ Branch 35 → 36 taken 51 times.
✓ Branch 35 → 37 taken 71 times.
|
122 | if (arg.text == QLatin1String(":zone")) |
| 357 | 51 | zone = value; | |
| 358 |
1/2✓ Branch 39 → 40 taken 71 times.
✗ Branch 39 → 41 not taken.
|
71 | else if (arg.text == QLatin1String(":value")) |
| 359 | 71 | relation = value; | |
| 360 | else | ||
| 361 | ✗ | return false; | |
| 362 | 122 | ++i; | |
| 363 | 122 | } | |
| 364 |
5/6✓ Branch 52 → 53 taken 71 times.
✗ Branch 52 → 57 not taken.
✓ Branch 56 → 57 taken 6 times.
✓ Branch 56 → 58 taken 65 times.
✓ Branch 59 → 60 taken 6 times.
✓ Branch 59 → 61 taken 65 times.
|
71 | if (positional.size() != 2 || positional.at(0) != QLatin1String("date")) |
| 365 | 6 | return false; | |
| 366 |
1/1✓ Branch 62 → 63 taken 65 times.
|
65 | const QDate day = QDate::fromString(positional.at(1), Qt::ISODate); |
| 367 |
6/9✓ Branch 63 → 64 taken 65 times.
✓ Branch 64 → 65 taken 65 times.
✗ Branch 64 → 71 not taken.
✓ Branch 67 → 68 taken 37 times.
✓ Branch 67 → 72 taken 28 times.
✗ Branch 70 → 71 not taken.
✓ Branch 70 → 72 taken 37 times.
✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 65 times.
|
65 | if (!day.isValid() || (relation != QLatin1String("ge") && relation != QLatin1String("le"))) |
| 368 | ✗ | return false; | |
| 369 |
4/6✓ Branch 76 → 77 taken 21 times.
✓ Branch 76 → 80 taken 44 times.
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 21 times.
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 83 taken 65 times.
|
65 | if (!vacation->zone.isEmpty() && vacation->zone != zone) |
| 370 | ✗ | return false; | |
| 371 | 65 | vacation->zone = zone; | |
| 372 |
2/2✓ Branch 86 → 87 taken 28 times.
✓ Branch 86 → 88 taken 37 times.
|
65 | QDate &target = relation == QLatin1String("ge") ? vacation->firstDay : vacation->lastDay; |
| 373 |
2/3✓ Branch 89 → 90 taken 65 times.
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 92 taken 65 times.
|
65 | if (target.isValid()) |
| 374 | ✗ | return false; | |
| 375 | 65 | target = day; | |
| 376 | 65 | return true; | |
| 377 | 71 | } | |
| 378 | |||
| 379 | 247 | bool decodeVacationCommand(const Command &command, Vacation *vacation) { | |
| 380 |
4/8✓ Branch 4 → 5 taken 247 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 247 times.
✗ Branch 5 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 247 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 247 times.
|
247 | if (command.name != QLatin1String("vacation") || command.hasBlock || !command.tests.isEmpty()) |
| 381 | ✗ | return false; | |
| 382 | 247 | const QList<Argument> &args = command.arguments; | |
| 383 |
3/6✓ Branch 13 → 14 taken 247 times.
✗ Branch 13 → 17 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 247 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 247 times.
|
247 | if (args.isEmpty() || !singleString(args.last(), &vacation->message)) |
| 384 | ✗ | return false; | |
| 385 | // S106-094: re-rendering must keep the meaning — no ":days" stays absent | ||
| 386 | // (days 0; the server default, RFC 5230 recommends 7), and a value the | ||
| 387 | // model cannot hold (0, above 365) or a repeated tag keeps the block | ||
| 388 | // advanced instead of being clamped or decided last-wins. | ||
| 389 | 247 | vacation->days = 0; | |
| 390 | 247 | QSet<QString> seenTags; | |
| 391 |
2/2✓ Branch 76 → 23 taken 624 times.
✓ Branch 76 → 77 taken 238 times.
|
862 | for (int i = 0; i < args.size() - 1; ++i) { |
| 392 | 624 | const Argument &arg = args.at(i); | |
| 393 |
3/6✓ Branch 24 → 25 taken 624 times.
✗ Branch 24 → 27 not taken.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 624 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 624 times.
|
624 | if (arg.kind != Argument::Kind::Tag || i + 1 >= args.size() - 1) |
| 394 | ✗ | return false; | |
| 395 |
2/2✓ Branch 32 → 33 taken 1 time.
✓ Branch 32 → 34 taken 623 times.
|
624 | if (seenTags.contains(arg.text)) |
| 396 | 1 | return false; | |
| 397 |
1/1✓ Branch 34 → 35 taken 623 times.
|
623 | seenTags.insert(arg.text); |
| 398 | 623 | const Argument &value = args.at(i + 1); | |
| 399 |
1/2✓ Branch 39 → 40 taken 239 times.
✗ Branch 39 → 43 not taken.
|
862 | if (arg.text == QLatin1String(":days") && value.kind == Argument::Kind::Number && |
| 400 |
8/8✓ Branch 38 → 39 taken 239 times.
✓ Branch 38 → 43 taken 384 times.
✓ Branch 40 → 41 taken 238 times.
✓ Branch 40 → 43 taken 1 time.
✓ Branch 41 → 42 taken 237 times.
✓ Branch 41 → 43 taken 1 time.
✓ Branch 44 → 45 taken 237 times.
✓ Branch 44 → 46 taken 386 times.
|
862 | value.number >= 1 && value.number <= 365) |
| 401 | 237 | vacation->days = static_cast<int>(value.number); | |
| 402 |
5/6✓ Branch 48 → 49 taken 230 times.
✓ Branch 48 → 52 taken 156 times.
✓ Branch 50 → 51 taken 230 times.
✗ Branch 50 → 52 not taken.
✓ Branch 53 → 54 taken 156 times.
✓ Branch 53 → 74 taken 230 times.
|
386 | else if (arg.text == QLatin1String(":subject") && singleString(value, &vacation->subject)) |
| 403 | ; | ||
| 404 |
5/6✓ Branch 56 → 57 taken 21 times.
✓ Branch 56 → 60 taken 135 times.
✓ Branch 58 → 59 taken 21 times.
✗ Branch 58 → 60 not taken.
✓ Branch 61 → 62 taken 135 times.
✓ Branch 61 → 74 taken 21 times.
|
156 | else if (arg.text == QLatin1String(":from") && singleString(value, &vacation->from)) |
| 405 | ; | ||
| 406 |
5/6✓ Branch 64 → 65 taken 127 times.
✓ Branch 64 → 67 taken 8 times.
✓ Branch 65 → 66 taken 127 times.
✗ Branch 65 → 67 not taken.
✓ Branch 68 → 69 taken 127 times.
✓ Branch 68 → 73 taken 8 times.
|
135 | else if (arg.text == QLatin1String(":addresses") && value.kind == Argument::Kind::StringList) |
| 407 |
1/1✓ Branch 69 → 70 taken 127 times.
|
127 | vacation->addresses = value.values(); |
| 408 | else | ||
| 409 | 8 | return false; | |
| 410 | 615 | ++i; | |
| 411 | } | ||
| 412 | 238 | return true; | |
| 413 | 247 | } | |
| 414 | |||
| 415 | 215 | bool decodeVacationTest(const Test &test, Vacation *vacation) { | |
| 416 |
6/8✓ Branch 4 → 5 taken 172 times.
✓ Branch 4 → 10 taken 43 times.
✓ Branch 6 → 7 taken 172 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 172 times.
✗ Branch 8 → 10 not taken.
✓ Branch 11 → 12 taken 172 times.
✓ Branch 11 → 13 taken 43 times.
|
215 | if (test.name == QLatin1String("true") && test.arguments.isEmpty() && test.tests.isEmpty()) |
| 417 | 172 | return true; | |
| 418 |
2/2✓ Branch 15 → 16 taken 28 times.
✓ Branch 15 → 36 taken 15 times.
|
43 | if (test.name == QLatin1String("allof")) { |
| 419 |
4/8✓ Branch 17 → 18 taken 28 times.
✗ Branch 17 → 22 not taken.
✓ Branch 19 → 20 taken 28 times.
✗ Branch 19 → 22 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 28 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 28 times.
|
28 | if (!test.arguments.isEmpty() || test.tests.isEmpty() || test.tests.size() > 2) |
| 420 | ✗ | return false; | |
| 421 |
2/2✓ Branch 34 → 28 taken 56 times.
✓ Branch 34 → 35 taken 28 times.
|
84 | for (const Test &operand : test.tests) |
| 422 |
2/3✓ Branch 29 → 30 taken 56 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 56 times.
|
56 | if (!decodeDateTest(operand, vacation)) |
| 423 | ✗ | return false; | |
| 424 | 28 | return true; | |
| 425 | } | ||
| 426 | 15 | return decodeDateTest(test, vacation); | |
| 427 | } | ||
| 428 | |||
| 429 | // --- block segmentation ------------------------------------------------------- | ||
| 430 | |||
| 431 | struct Group { | ||
| 432 | int first = 0; // command index | ||
| 433 | int last = 0; | ||
| 434 | }; | ||
| 435 | |||
| 436 | // The `if` test hidden in Roundcube's disabled form "if false # <test>". | ||
| 437 | // A test spanning lines continues on "# " lines (renderIfHeader()). The | ||
| 438 | // form is only recognised when re-rendering it loses nothing: no comment | ||
| 439 | // before "false", only the hidden test's comment lines (and blank lines | ||
| 440 | // after them) up to "{", and no comment inside the hidden test (S106-095). | ||
| 441 | 72594 | bool disabledTest(const QByteArray &source, const Command &command, Test *test) { | |
| 442 | 72594 | const Test &visible = command.tests.first(); | |
| 443 |
5/6✓ Branch 5 → 6 taken 568 times.
✓ Branch 5 → 10 taken 72026 times.
✓ Branch 7 → 8 taken 568 times.
✗ Branch 7 → 10 not taken.
✓ Branch 12 → 13 taken 72026 times.
✓ Branch 12 → 14 taken 568 times.
|
73162 | if (visible.name != QLatin1String("false") || !visible.arguments.isEmpty() || |
| 444 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 568 times.
|
568 | !visible.tests.isEmpty()) |
| 445 | 72026 | return false; | |
| 446 |
4/4✓ Branch 14 → 15 taken 568 times.
✓ Branch 15 → 16 taken 568 times.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 19 taken 567 times.
|
568 | if (hasComment(source.mid(command.span.begin, visible.span.end - command.span.begin))) |
| 447 | 1 | return false; | |
| 448 | const QByteArray between = | ||
| 449 |
1/1✓ Branch 19 → 20 taken 567 times.
|
567 | source.mid(visible.span.end, command.blockSpan.begin - visible.span.end); |
| 450 |
1/1✓ Branch 20 → 21 taken 567 times.
|
567 | const QList<QByteArray> lines = between.split('\n'); |
| 451 |
1/1✓ Branch 22 → 23 taken 567 times.
|
567 | const QByteArray first = lines.first().trimmed(); |
| 452 |
3/3✓ Branch 23 → 24 taken 567 times.
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 26 taken 566 times.
|
567 | if (!first.startsWith('#')) |
| 453 | 1 | return false; | |
| 454 |
2/2✓ Branch 26 → 27 taken 566 times.
✓ Branch 27 → 28 taken 566 times.
|
566 | QByteArray comment = first.mid(1).trimmed(); |
| 455 | // A CRLF script renders its continuation lines with CRLF: the '\r' ends | ||
| 456 | // the comment line, it is not part of the hidden test (a multi-line | ||
| 457 | // string's "." terminator would read ".\r"; fuzz_sieve_script). | ||
| 458 |
4/4✓ Branch 30 → 31 taken 566 times.
✓ Branch 31 → 32 taken 4 times.
✓ Branch 31 → 33 taken 562 times.
✓ Branch 34 → 35 taken 566 times.
|
566 | QByteArray lineBreak = lines.first().endsWith('\r') ? "\r\n" : "\n"; |
| 459 | 566 | bool blankSeen = false; | |
| 460 |
2/2✓ Branch 67 → 36 taken 570 times.
✓ Branch 67 → 68 taken 565 times.
|
1135 | for (int i = 1; i < lines.size(); ++i) { |
| 461 | 570 | const QByteArray &line = lines.at(i); | |
| 462 |
3/3✓ Branch 37 → 38 taken 570 times.
✓ Branch 40 → 41 taken 566 times.
✓ Branch 40 → 42 taken 4 times.
|
570 | if (line.trimmed().isEmpty()) { |
| 463 | 566 | blankSeen = true; | |
| 464 | 566 | continue; | |
| 465 | } | ||
| 466 |
6/7✓ Branch 42 → 43 taken 4 times.
✗ Branch 42 → 45 not taken.
✓ Branch 43 → 44 taken 4 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 3 times.
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 49 taken 3 times.
|
4 | if (blankSeen || !line.startsWith('#')) |
| 467 | 1 | return false; | |
| 468 |
1/1✓ Branch 49 → 50 taken 3 times.
|
3 | QByteArray continuation = line.mid(1); |
| 469 |
2/3✓ Branch 50 → 51 taken 3 times.
✓ Branch 51 → 52 taken 3 times.
✗ Branch 51 → 53 not taken.
|
3 | if (continuation.startsWith(' ')) |
| 470 |
1/1✓ Branch 52 → 53 taken 3 times.
|
3 | continuation.remove(0, 1); |
| 471 |
1/1✓ Branch 53 → 54 taken 3 times.
|
3 | const bool crlf = continuation.endsWith('\r'); |
| 472 |
1/2✗ Branch 54 → 55 not taken.
✓ Branch 54 → 56 taken 3 times.
|
3 | if (crlf) |
| 473 | ✗ | continuation.chop(1); | |
| 474 |
2/2✓ Branch 56 → 57 taken 3 times.
✓ Branch 57 → 58 taken 3 times.
|
3 | comment += lineBreak + continuation; |
| 475 |
2/3✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 3 times.
✓ Branch 62 → 63 taken 3 times.
|
3 | lineBreak = crlf ? "\r\n" : "\n"; |
| 476 | 3 | } | |
| 477 |
3/3✓ Branch 68 → 69 taken 565 times.
✓ Branch 69 → 70 taken 2 times.
✓ Branch 69 → 71 taken 563 times.
|
565 | if (hasComment(comment)) |
| 478 | 2 | return false; | |
| 479 |
1/1✓ Branch 71 → 72 taken 563 times.
|
563 | return parseTestExpression(comment, test); |
| 480 | 567 | } | |
| 481 | |||
| 482 | 92729 | bool decodeIfBlock(const QByteArray &source, const Command &command, Block *block) { | |
| 483 |
2/4✓ Branch 6 → 7 taken 72594 times.
✗ Branch 6 → 11 not taken.
✓ Branch 8 → 9 taken 72594 times.
✗ Branch 8 → 11 not taken.
|
237917 | if (command.name != QLatin1String("if") || !command.arguments.isEmpty() || |
| 484 |
6/8✓ Branch 4 → 5 taken 72594 times.
✓ Branch 4 → 11 taken 20135 times.
✓ Branch 9 → 10 taken 72594 times.
✗ Branch 9 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 72594 times.
✓ Branch 13 → 14 taken 20135 times.
✓ Branch 13 → 15 taken 72594 times.
|
237917 | command.tests.size() != 1 || command.testList || !command.hasBlock) |
| 485 | 20135 | return false; | |
| 486 | 72594 | Test test = command.tests.first(); | |
| 487 | 72594 | bool enabled = true; | |
| 488 | 72594 | Test hidden; | |
| 489 |
3/3✓ Branch 17 → 18 taken 72594 times.
✓ Branch 18 → 19 taken 563 times.
✓ Branch 18 → 20 taken 72031 times.
|
72594 | if (disabledTest(source, command, &hidden)) { |
| 490 | 563 | enabled = false; | |
| 491 | 563 | test = hidden; | |
| 492 |
4/4✓ Branch 21 → 22 taken 72031 times.
✓ Branch 22 → 23 taken 72031 times.
✓ Branch 24 → 25 taken 13 times.
✓ Branch 24 → 26 taken 72018 times.
|
72031 | } else if (hasComment(source.mid(command.span.begin, command.span.length()))) { |
| 493 | // Comments are never re-rendered, so a commented rule stays advanced. | ||
| 494 | 13 | return false; | |
| 495 | } | ||
| 496 |
8/12✓ Branch 26 → 27 taken 563 times.
✓ Branch 26 → 32 taken 72018 times.
✓ Branch 28 → 29 taken 563 times.
✓ Branch 29 → 30 taken 563 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 563 times.
✓ Branch 33 → 34 taken 563 times.
✓ Branch 33 → 35 taken 72018 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 72581 times.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 83 not taken.
|
72581 | if (!enabled && hasComment(source.mid(command.blockSpan.begin, command.blockSpan.length()))) |
| 497 | ✗ | return false; | |
| 498 |
6/6✓ Branch 38 → 39 taken 8444 times.
✓ Branch 38 → 44 taken 64137 times.
✓ Branch 42 → 43 taken 215 times.
✓ Branch 42 → 44 taken 8229 times.
✓ Branch 45 → 46 taken 215 times.
✓ Branch 45 → 61 taken 72366 times.
|
72581 | if (command.block.size() == 1 && command.block.first().name == QLatin1String("vacation")) { |
| 499 | 215 | Vacation vacation; | |
| 500 |
1/1✓ Branch 46 → 47 taken 215 times.
|
215 | if (decodeVacationTest(test, &vacation) && |
| 501 |
8/10✓ Branch 47 → 48 taken 209 times.
✓ Branch 47 → 54 taken 6 times.
✓ Branch 49 → 50 taken 209 times.
✓ Branch 50 → 51 taken 209 times.
✗ Branch 50 → 54 not taken.
✓ Branch 51 → 52 taken 209 times.
✓ Branch 52 → 53 taken 209 times.
✗ Branch 52 → 54 not taken.
✓ Branch 55 → 56 taken 209 times.
✓ Branch 55 → 58 taken 6 times.
|
215 | decodeVacationCommand(command.block.first(), &vacation) && representable(vacation)) { |
| 502 | 209 | vacation.enabled = enabled; | |
| 503 | 209 | block->kind = BlockKind::Vacation; | |
| 504 | 209 | block->vacation = vacation; | |
| 505 | 209 | return true; | |
| 506 | } | ||
| 507 | 6 | return false; | |
| 508 | 215 | } | |
| 509 | 72366 | Rule rule; | |
| 510 |
11/11✓ Branch 61 → 62 taken 72366 times.
✓ Branch 62 → 63 taken 72153 times.
✓ Branch 62 → 67 taken 213 times.
✓ Branch 63 → 64 taken 72153 times.
✓ Branch 64 → 65 taken 72131 times.
✓ Branch 64 → 67 taken 22 times.
✓ Branch 65 → 66 taken 72131 times.
✓ Branch 66 → 67 taken 2 times.
✓ Branch 66 → 68 taken 72129 times.
✓ Branch 69 → 70 taken 237 times.
✓ Branch 69 → 71 taken 72129 times.
|
72366 | if (!decodeTest(test, &rule) || !decodeActions(command.block, &rule) || !representable(rule)) |
| 511 | 237 | return false; | |
| 512 | 72129 | rule.enabled = enabled; | |
| 513 | 72129 | block->kind = BlockKind::Rule; | |
| 514 | 72129 | block->rule = rule; | |
| 515 | 72129 | return true; | |
| 516 | 72594 | } | |
| 517 | |||
| 518 | 3263 | QList<Group> groupCommands(const QList<Command> &commands) { | |
| 519 | 3263 | QList<Group> groups; | |
| 520 |
2/2✓ Branch 24 → 3 taken 95127 times.
✓ Branch 24 → 25 taken 3263 times.
|
98390 | for (int i = 0; i < commands.size(); ++i) { |
| 521 | 95127 | const QString &name = commands.at(i).name; | |
| 522 |
4/4✓ Branch 6 → 7 taken 95104 times.
✓ Branch 6 → 10 taken 23 times.
✓ Branch 9 → 10 taken 23 times.
✓ Branch 9 → 11 taken 95081 times.
|
95127 | const bool continues = name == QLatin1String("elsif") || name == QLatin1String("else"); |
| 523 |
5/6✓ Branch 12 → 13 taken 46 times.
✓ Branch 12 → 16 taken 95081 times.
✓ Branch 14 → 15 taken 46 times.
✗ Branch 14 → 16 not taken.
✓ Branch 17 → 18 taken 46 times.
✓ Branch 17 → 20 taken 95081 times.
|
95127 | if (continues && !groups.isEmpty()) |
| 524 |
1/1✓ Branch 18 → 19 taken 46 times.
|
46 | groups.last().last = i; |
| 525 | else | ||
| 526 |
1/1✓ Branch 20 → 21 taken 95081 times.
|
95081 | groups.append({i, i}); |
| 527 | } | ||
| 528 | 3263 | return groups; | |
| 529 | ✗ | } | |
| 530 | |||
| 531 | // End of a block: after the command, trailing blanks, an optional same-line | ||
| 532 | // comment and one line end. | ||
| 533 | 95081 | int blockEnd(const QByteArray &source, int commandEnd) { | |
| 534 | 95081 | int pos = commandEnd; | |
| 535 |
7/8✓ Branch 5 → 6 taken 95057 times.
✓ Branch 5 → 11 taken 38 times.
✓ Branch 7 → 8 taken 95043 times.
✓ Branch 7 → 10 taken 14 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 95043 times.
✓ Branch 12 → 3 taken 14 times.
✓ Branch 12 → 13 taken 95081 times.
|
95095 | while (pos < source.size() && (source.at(pos) == ' ' || source.at(pos) == '\t')) |
| 536 | 14 | ++pos; | |
| 537 |
6/6✓ Branch 14 → 15 taken 95043 times.
✓ Branch 14 → 18 taken 38 times.
✓ Branch 16 → 17 taken 8 times.
✓ Branch 16 → 18 taken 95035 times.
✓ Branch 19 → 20 taken 8 times.
✓ Branch 19 → 26 taken 95073 times.
|
95081 | if (pos < source.size() && source.at(pos) == '#') { |
| 538 | 8 | const int lineEnd = source.indexOf('\n', pos); | |
| 539 |
1/2✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 8 times.
|
8 | return lineEnd < 0 ? source.size() : lineEnd + 1; |
| 540 | } | ||
| 541 |
8/10✓ Branch 27 → 28 taken 95035 times.
✓ Branch 27 → 35 taken 38 times.
✓ Branch 29 → 30 taken 54 times.
✓ Branch 29 → 35 taken 94981 times.
✓ Branch 31 → 32 taken 54 times.
✗ Branch 31 → 35 not taken.
✓ Branch 33 → 34 taken 54 times.
✗ Branch 33 → 35 not taken.
✓ Branch 36 → 37 taken 54 times.
✓ Branch 36 → 38 taken 95019 times.
|
95127 | if (pos < source.size() && source.at(pos) == '\r' && pos + 1 < source.size() && |
| 542 | 54 | source.at(pos + 1) == '\n') | |
| 543 | 54 | return pos + 2; | |
| 544 |
6/6✓ Branch 39 → 40 taken 94981 times.
✓ Branch 39 → 43 taken 38 times.
✓ Branch 41 → 42 taken 94975 times.
✓ Branch 41 → 43 taken 6 times.
✓ Branch 44 → 45 taken 94975 times.
✓ Branch 44 → 46 taken 44 times.
|
95019 | if (pos < source.size() && source.at(pos) == '\n') |
| 545 | 94975 | return pos + 1; | |
| 546 | 44 | return commandEnd; | |
| 547 | } | ||
| 548 | |||
| 549 | // Splits the leading bytes into (kept prefix, marker name). The marker must | ||
| 550 | // be the last non-blank line before the command. | ||
| 551 | 95081 | bool splitMarker(const QByteArray &leading, QByteArray *prefix, QString *name) { | |
| 552 |
4/7✓ Branch 2 → 3 taken 13 times.
✓ Branch 2 → 15 taken 95068 times.
✓ Branch 4 → 5 taken 13 times.
✗ Branch 4 → 15 not taken.
✓ Branch 9 → 10 taken 13 times.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
|
95094 | static const QRegularExpression marker(QStringLiteral("^#\\s*rule:\\[(.*)\\]\\s*$")); |
| 553 | 95081 | QByteArray trimmed = leading; | |
| 554 |
9/10✓ Branch 19 → 20 taken 72777 times.
✓ Branch 19 → 25 taken 22314 times.
✓ Branch 20 → 21 taken 72777 times.
✓ Branch 21 → 22 taken 72767 times.
✓ Branch 21 → 24 taken 10 times.
✓ Branch 22 → 23 taken 72767 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 72767 times.
✓ Branch 26 → 17 taken 10 times.
✓ Branch 26 → 27 taken 95081 times.
|
95091 | while (!trimmed.isEmpty() && (trimmed.endsWith(' ') || trimmed.endsWith('\t'))) |
| 555 |
1/1✓ Branch 17 → 18 taken 10 times.
|
10 | trimmed.chop(1); |
| 556 |
3/3✓ Branch 27 → 28 taken 95081 times.
✓ Branch 28 → 29 taken 72767 times.
✓ Branch 28 → 30 taken 22314 times.
|
95081 | if (trimmed.endsWith('\n')) |
| 557 |
1/1✓ Branch 29 → 30 taken 72767 times.
|
72767 | trimmed.chop(1); |
| 558 |
3/3✓ Branch 30 → 31 taken 95081 times.
✓ Branch 31 → 32 taken 28 times.
✓ Branch 31 → 33 taken 95053 times.
|
95081 | if (trimmed.endsWith('\r')) |
| 559 |
1/1✓ Branch 32 → 33 taken 28 times.
|
28 | trimmed.chop(1); |
| 560 | 95081 | const int lineStart = trimmed.lastIndexOf('\n') + 1; | |
| 561 |
2/2✓ Branch 34 → 35 taken 95081 times.
✓ Branch 35 → 36 taken 95081 times.
|
95081 | const QString line = QString::fromUtf8(trimmed.mid(lineStart)); |
| 562 |
1/1✓ Branch 38 → 39 taken 95081 times.
|
95081 | const QRegularExpressionMatch match = marker.match(line); |
| 563 |
3/3✓ Branch 39 → 40 taken 95081 times.
✓ Branch 40 → 41 taken 53169 times.
✓ Branch 40 → 42 taken 41912 times.
|
95081 | if (!match.hasMatch()) |
| 564 | 53169 | return false; | |
| 565 |
1/1✓ Branch 42 → 43 taken 41912 times.
|
41912 | *prefix = leading.left(lineStart); |
| 566 |
1/1✓ Branch 45 → 46 taken 41912 times.
|
41912 | *name = match.captured(1); |
| 567 | 41912 | return true; | |
| 568 | 95081 | } | |
| 569 | |||
| 570 | 95053 | bool isRequireCommand(const Command &command, QStringList *extensions) { | |
| 571 |
3/6✓ Branch 5 → 6 taken 2324 times.
✗ Branch 5 → 12 not taken.
✓ Branch 7 → 8 taken 2324 times.
✗ Branch 7 → 12 not taken.
✓ Branch 9 → 10 taken 2324 times.
✗ Branch 9 → 12 not taken.
|
99701 | if (command.name != QLatin1String("require") || command.hasBlock || !command.tests.isEmpty() || |
| 572 |
5/6✓ Branch 4 → 5 taken 2324 times.
✓ Branch 4 → 12 taken 92729 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 2324 times.
✓ Branch 14 → 15 taken 92729 times.
✓ Branch 14 → 16 taken 2324 times.
|
99701 | command.arguments.size() != 1 || command.arguments.first().kind != Argument::Kind::StringList) |
| 573 | 92729 | return false; | |
| 574 |
1/1✓ Branch 17 → 18 taken 2324 times.
|
2324 | *extensions = command.arguments.first().values(); |
| 575 | 2324 | return true; | |
| 576 | } | ||
| 577 | |||
| 578 | 3263 | Style detectStyle(const Script &script, const QList<Block> &blocks) { | |
| 579 | 3263 | Style style; | |
| 580 |
4/4✓ Branch 5 → 6 taken 29 times.
✓ Branch 5 → 7 taken 3234 times.
✓ Branch 6 → 8 taken 29 times.
✓ Branch 7 → 8 taken 3234 times.
|
3263 | style.lineEnd = script.source.contains("\r\n") ? QByteArray("\r\n") : QByteArray("\n"); |
| 581 |
2/2✓ Branch 48 → 12 taken 25303 times.
✓ Branch 48 → 49 taken 391 times.
|
25694 | for (const Command &command : script.commands) { |
| 582 |
7/10✓ Branch 15 → 16 taken 2872 times.
✓ Branch 15 → 21 taken 22431 times.
✓ Branch 16 → 17 taken 2872 times.
✗ Branch 16 → 21 not taken.
✓ Branch 18 → 19 taken 2872 times.
✗ Branch 18 → 21 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 2872 times.
✓ Branch 23 → 24 taken 22431 times.
✓ Branch 23 → 25 taken 2872 times.
|
28175 | if (command.name != QLatin1String("if") || !command.hasBlock || command.block.isEmpty() || |
| 583 | 2872 | command.tests.isEmpty()) | |
| 584 | 22431 | continue; | |
| 585 | 2872 | const int inner = command.block.first().span.begin; | |
| 586 | 2872 | const int lineStart = script.source.lastIndexOf('\n', inner - 1) + 1; | |
| 587 |
1/1✓ Branch 27 → 28 taken 2872 times.
|
2872 | const QByteArray indent = script.source.mid(lineStart, inner - lineStart); |
| 588 |
7/11✓ Branch 29 → 30 taken 2872 times.
✗ Branch 29 → 34 not taken.
✓ Branch 30 → 31 taken 2872 times.
✓ Branch 32 → 33 taken 2682 times.
✓ Branch 32 → 34 taken 190 times.
✓ Branch 35 → 36 taken 2872 times.
✗ Branch 35 → 37 not taken.
✓ Branch 37 → 38 taken 2682 times.
✓ Branch 37 → 39 taken 190 times.
✗ Branch 67 → 68 not taken.
✗ Branch 67 → 69 not taken.
|
2872 | if (!indent.isEmpty() && indent.trimmed().isEmpty()) |
| 589 | 2682 | style.indent = indent; | |
| 590 | 5744 | const QByteArray between = script.source.mid(command.tests.first().span.end, | |
| 591 |
1/1✓ Branch 41 → 42 taken 2872 times.
|
2872 | command.blockSpan.begin - command.tests.first().span.end); |
| 592 |
1/1✓ Branch 42 → 43 taken 2872 times.
|
2872 | style.braceOnOwnLine = between.contains('\n'); |
| 593 | 2872 | break; | |
| 594 | 2872 | } | |
| 595 | 3263 | int rules = 0; | |
| 596 | 3263 | int withStop = 0; | |
| 597 |
2/2✓ Branch 58 → 51 taken 95192 times.
✓ Branch 58 → 59 taken 3263 times.
|
98455 | for (const Block &block : blocks) { |
| 598 |
2/2✓ Branch 52 → 53 taken 23063 times.
✓ Branch 52 → 54 taken 72129 times.
|
95192 | if (block.kind != BlockKind::Rule) |
| 599 | 23063 | continue; | |
| 600 | 72129 | ++rules; | |
| 601 |
2/2✓ Branch 54 → 55 taken 63921 times.
✓ Branch 54 → 56 taken 8208 times.
|
72129 | if (block.rule.stop) |
| 602 | 63921 | ++withStop; | |
| 603 | } | ||
| 604 |
4/4✓ Branch 59 → 60 taken 2759 times.
✓ Branch 59 → 62 taken 504 times.
✓ Branch 60 → 61 taken 1196 times.
✓ Branch 60 → 62 taken 1563 times.
|
3263 | style.stopByDefault = rules > 0 && withStop * 2 > rules; |
| 605 | 3263 | return style; | |
| 606 | ✗ | } | |
| 607 | |||
| 608 | 20390 | bool endsTerminal(const Command &command) { | |
| 609 |
6/6✓ Branch 4 → 5 taken 20389 times.
✓ Branch 4 → 11 taken 1 time.
✓ Branch 7 → 8 taken 20340 times.
✓ Branch 7 → 11 taken 49 times.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 20339 times.
|
40730 | return command.name == QLatin1String("stop") || command.name == QLatin1String("keep") || |
| 610 | 40730 | command.name == QLatin1String("discard"); | |
| 611 | } | ||
| 612 | |||
| 613 | } // namespace | ||
| 614 | |||
| 615 | 3314 | Document decode(const QByteArray &source) { | |
| 616 |
1/1✓ Branch 2 → 3 taken 3314 times.
|
3314 | Document document; |
| 617 | 3314 | document.source = source; | |
| 618 |
1/1✓ Branch 4 → 5 taken 3314 times.
|
3314 | const Script script = parse(source); |
| 619 |
2/2✓ Branch 6 → 7 taken 51 times.
✓ Branch 6 → 22 taken 3263 times.
|
3314 | if (!script.isValid()) { |
| 620 | 51 | document.readOnly = true; | |
| 621 | 51 | document.error = script.error; | |
| 622 | 51 | Block block; | |
| 623 | 51 | block.kind = BlockKind::Advanced; | |
| 624 | 51 | block.original = source; | |
| 625 | 51 | block.originalIndex = 0; | |
| 626 | 51 | block.line = 1; | |
| 627 |
1/1✓ Branch 9 → 10 taken 51 times.
|
51 | document.blocks.append(block); |
| 628 |
5/5✓ Branch 10 → 11 taken 51 times.
✓ Branch 14 → 15 taken 45 times.
✓ Branch 15 → 16 taken 45 times.
✓ Branch 19 → 12 taken 45 times.
✓ Branch 19 → 20 taken 51 times.
|
96 | qCInfo(lcSieveCodec) << "Script does not parse; opened read-only"; |
| 629 | 51 | return document; | |
| 630 | 51 | } | |
| 631 | 3263 | int cursor = 0; | |
| 632 |
1/1✓ Branch 22 → 23 taken 3263 times.
|
3263 | const QList<Group> groups = groupCommands(script.commands); |
| 633 |
2/2✓ Branch 81 → 25 taken 95081 times.
✓ Branch 81 → 82 taken 3263 times.
|
98344 | for (const Group &group : groups) { |
| 634 | 95081 | const Command &first = script.commands.at(group.first); | |
| 635 | 95081 | const Command &last = script.commands.at(group.last); | |
| 636 | 95081 | const int end = blockEnd(source, last.span.end); | |
| 637 | 95081 | Block block; | |
| 638 |
1/1✓ Branch 29 → 30 taken 95081 times.
|
95081 | block.original = source.mid(cursor, end - cursor); |
| 639 | 95081 | block.originalIndex = document.blocks.size(); | |
| 640 | 95081 | block.line = first.line; | |
| 641 |
1/1✓ Branch 33 → 34 taken 95081 times.
|
95081 | const QByteArray leading = source.mid(cursor, first.span.begin - cursor); |
| 642 | 95081 | QByteArray prefix; | |
| 643 | 95081 | QString name; | |
| 644 |
3/3✓ Branch 34 → 35 taken 95081 times.
✓ Branch 35 → 36 taken 41912 times.
✓ Branch 35 → 38 taken 53169 times.
|
95081 | if (splitMarker(leading, &prefix, &name)) { |
| 645 | 41912 | block.named = true; | |
| 646 | 41912 | block.name = name; | |
| 647 | 41912 | block.leading = prefix; | |
| 648 | } else { | ||
| 649 | 53169 | block.leading = leading; | |
| 650 | } | ||
| 651 | 95081 | QStringList extensions; | |
| 652 |
7/7✓ Branch 39 → 40 taken 95053 times.
✓ Branch 39 → 43 taken 28 times.
✓ Branch 40 → 41 taken 95053 times.
✓ Branch 41 → 42 taken 2324 times.
✓ Branch 41 → 43 taken 92729 times.
✓ Branch 44 → 45 taken 2324 times.
✓ Branch 44 → 46 taken 92757 times.
|
95081 | if (group.first == group.last && isRequireCommand(first, &extensions)) { |
| 653 | 2324 | block.kind = BlockKind::Require; | |
| 654 | 2324 | block.extensions = extensions; | |
| 655 |
7/7✓ Branch 46 → 47 taken 92729 times.
✓ Branch 46 → 50 taken 28 times.
✓ Branch 47 → 48 taken 92729 times.
✓ Branch 48 → 49 taken 72338 times.
✓ Branch 48 → 50 taken 20391 times.
✓ Branch 51 → 52 taken 72338 times.
✓ Branch 51 → 53 taken 20419 times.
|
92757 | } else if (group.first == group.last && decodeIfBlock(source, first, &block)) { |
| 656 | 72338 | block.rule.name = block.name; | |
| 657 |
2/2✓ Branch 56 → 57 taken 38 times.
✓ Branch 56 → 66 taken 20353 times.
|
20391 | } else if (group.first == group.last && first.name == QLatin1String("vacation") && |
| 658 |
5/8✓ Branch 58 → 59 taken 38 times.
✓ Branch 59 → 60 taken 38 times.
✓ Branch 60 → 61 taken 38 times.
✗ Branch 60 → 66 not taken.
✓ Branch 69 → 70 taken 29 times.
✓ Branch 69 → 71 taken 20390 times.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
|
20457 | !hasComment(source.mid(first.span.begin, first.span.length())) && |
| 659 |
9/10✓ Branch 53 → 54 taken 20391 times.
✓ Branch 53 → 66 taken 28 times.
✓ Branch 61 → 62 taken 38 times.
✓ Branch 62 → 63 taken 29 times.
✓ Branch 62 → 66 taken 9 times.
✓ Branch 63 → 64 taken 29 times.
✓ Branch 64 → 65 taken 29 times.
✗ Branch 64 → 66 not taken.
✓ Branch 67 → 68 taken 38 times.
✓ Branch 67 → 69 taken 20381 times.
|
40810 | decodeVacationCommand(first, &block.vacation) && representable(block.vacation)) { |
| 660 | 29 | block.kind = BlockKind::Vacation; | |
| 661 | } else { | ||
| 662 | 20390 | block.kind = BlockKind::Advanced; | |
| 663 | 20390 | block.terminal = endsTerminal(last); | |
| 664 | } | ||
| 665 |
1/1✓ Branch 73 → 74 taken 95081 times.
|
95081 | document.blocks.append(block); |
| 666 | 95081 | cursor = end; | |
| 667 | 95081 | } | |
| 668 |
2/2✓ Branch 83 → 84 taken 111 times.
✓ Branch 83 → 91 taken 3152 times.
|
3263 | if (cursor < source.size()) { |
| 669 | 111 | Block tail; | |
| 670 | 111 | tail.kind = BlockKind::Comment; | |
| 671 |
1/1✓ Branch 84 → 85 taken 111 times.
|
111 | tail.original = source.mid(cursor); |
| 672 | 111 | tail.originalIndex = document.blocks.size(); | |
| 673 |
1/1✓ Branch 88 → 89 taken 111 times.
|
111 | document.blocks.append(tail); |
| 674 | 111 | } | |
| 675 |
1/1✓ Branch 91 → 92 taken 3263 times.
|
3263 | document.style = detectStyle(script, document.blocks); |
| 676 |
8/8✓ Branch 94 → 95 taken 3263 times.
✓ Branch 98 → 99 taken 2782 times.
✓ Branch 99 → 100 taken 2782 times.
✓ Branch 101 → 102 taken 2782 times.
✓ Branch 102 → 103 taken 2782 times.
✓ Branch 104 → 105 taken 2782 times.
✓ Branch 109 → 96 taken 2782 times.
✓ Branch 109 → 110 taken 3263 times.
|
6045 | qCDebug(lcSieveCodec) << "Decoded" << document.blocks.size() << "blocks from" << source.size() |
| 677 |
1/1✓ Branch 105 → 106 taken 2782 times.
|
2782 | << "bytes"; |
| 678 | 3263 | return document; | |
| 679 | 3314 | } | |
| 680 | |||
| 681 | // --- encoding ----------------------------------------------------------------- | ||
| 682 | |||
| 683 | namespace { | ||
| 684 | |||
| 685 | 636 | bool extensionPresent(const QSet<QString> &present, const QString &extension) { | |
| 686 |
2/2✓ Branch 3 → 4 taken 363 times.
✓ Branch 3 → 5 taken 273 times.
|
636 | if (present.contains(extension)) |
| 687 | 363 | return true; | |
| 688 | // Legacy "imapflags" offers the same flag commands (reference server). | ||
| 689 |
8/8✓ Branch 7 → 8 taken 31 times.
✓ Branch 7 → 14 taken 242 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 29 times.
✓ Branch 16 → 17 taken 31 times.
✓ Branch 16 → 18 taken 242 times.
✓ Branch 18 → 19 taken 31 times.
✓ Branch 18 → 20 taken 242 times.
|
304 | return extension == QLatin1String("imap4flags") && present.contains(QStringLiteral("imapflags")); |
| 690 | } | ||
| 691 | |||
| 692 | 13 | QByteArray patchRequire(const QByteArray &bytes, const QStringList &missing) { | |
| 693 | 13 | return addRequirements(bytes, missing); | |
| 694 | } | ||
| 695 | |||
| 696 | 22789 | QByteArray blockBytes(const Block &block, const Style &style) { | |
| 697 |
2/2✓ Branch 2 → 3 taken 22231 times.
✓ Branch 2 → 4 taken 558 times.
|
22789 | if (!block.dirty) |
| 698 | 22231 | return block.original; | |
| 699 |
2/2✓ Branch 4 → 5 taken 542 times.
✓ Branch 4 → 10 taken 16 times.
|
558 | if (block.kind == BlockKind::Rule) |
| 700 |
2/2✓ Branch 5 → 6 taken 542 times.
✓ Branch 6 → 7 taken 542 times.
|
542 | return block.leading + renderRule(block.rule, style); |
| 701 |
1/2✓ Branch 10 → 11 taken 16 times.
✗ Branch 10 → 16 not taken.
|
16 | if (block.kind == BlockKind::Vacation) |
| 702 |
2/2✓ Branch 11 → 12 taken 16 times.
✓ Branch 12 → 13 taken 16 times.
|
16 | return block.leading + renderVacation(block.vacation, block.name, style); |
| 703 | ✗ | return block.original; | |
| 704 | } | ||
| 705 | |||
| 706 | 22 | void collectFileInto(const QList<Command> &commands, QList<StringValue> *targets) { | |
| 707 |
2/2✓ Branch 24 → 4 taken 18 times.
✓ Branch 24 → 25 taken 22 times.
|
40 | for (const Command &command : commands) { |
| 708 |
1/2✓ Branch 9 → 10 taken 4 times.
✗ Branch 9 → 16 not taken.
|
22 | if (command.name == QLatin1String("fileinto") && !command.arguments.isEmpty() && |
| 709 |
6/8✓ Branch 7 → 8 taken 4 times.
✓ Branch 7 → 16 taken 14 times.
✓ Branch 11 → 12 taken 4 times.
✗ Branch 11 → 16 not taken.
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 16 not taken.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 21 taken 14 times.
|
26 | command.arguments.last().kind == Argument::Kind::StringList && |
| 710 | 4 | command.arguments.last().strings.size() == 1) | |
| 711 |
1/1✓ Branch 20 → 21 taken 4 times.
|
4 | targets->append(command.arguments.last().strings.first()); |
| 712 |
1/1✓ Branch 21 → 22 taken 18 times.
|
18 | collectFileInto(command.block, targets); |
| 713 | } | ||
| 714 | 22 | } | |
| 715 | |||
| 716 | 20 | bool renamedTarget(const QString &target, const QString &oldMailbox, const QString &newMailbox, | |
| 717 | QChar delimiter, QString *result) { | ||
| 718 |
2/2✓ Branch 3 → 4 taken 8 times.
✓ Branch 3 → 6 taken 12 times.
|
20 | if (target == oldMailbox) { |
| 719 | 8 | *result = newMailbox; | |
| 720 | 8 | return true; | |
| 721 | } | ||
| 722 |
1/1✓ Branch 6 → 7 taken 12 times.
|
12 | const QString prefix = oldMailbox + delimiter; |
| 723 |
3/3✓ Branch 7 → 8 taken 12 times.
✓ Branch 8 → 9 taken 8 times.
✓ Branch 8 → 10 taken 4 times.
|
12 | if (!target.startsWith(prefix)) |
| 724 | 8 | return false; | |
| 725 |
3/3✓ Branch 11 → 12 taken 4 times.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 13 → 14 taken 4 times.
|
4 | *result = newMailbox + delimiter + target.mid(prefix.size()); |
| 726 | 4 | return true; | |
| 727 | 12 | } | |
| 728 | |||
| 729 | } // namespace | ||
| 730 | |||
| 731 | 902 | QByteArray encode(const Document &document) { | |
| 732 |
2/2✓ Branch 2 → 3 taken 7 times.
✓ Branch 2 → 4 taken 895 times.
|
902 | if (document.readOnly) |
| 733 | 7 | return document.source; | |
| 734 |
1/1✓ Branch 4 → 5 taken 895 times.
|
895 | const QStringList required = document.requiredExtensions(); |
| 735 |
1/1✓ Branch 7 → 8 taken 895 times.
|
895 | const QSet<QString> present(required.cbegin(), required.cend()); |
| 736 | 895 | QStringList missing; | |
| 737 |
2/2✓ Branch 34 → 10 taken 22789 times.
✓ Branch 34 → 35 taken 895 times.
|
23684 | for (const Block &block : document.blocks) { |
| 738 |
2/2✓ Branch 11 → 12 taken 22231 times.
✓ Branch 11 → 13 taken 558 times.
|
22789 | if (!block.dirty) |
| 739 | 22231 | continue; | |
| 740 |
2/2✓ Branch 13 → 14 taken 16 times.
✓ Branch 13 → 15 taken 542 times.
|
558 | const QStringList needed = block.kind == BlockKind::Vacation ? extensionsFor(block.vacation) |
| 741 |
2/2✓ Branch 14 → 16 taken 16 times.
✓ Branch 15 → 16 taken 542 times.
|
558 | : extensionsFor(block.rule); |
| 742 |
2/2✓ Branch 29 → 18 taken 636 times.
✓ Branch 29 → 30 taken 558 times.
|
1194 | for (const QString &extension : needed) |
| 743 |
7/7✓ Branch 19 → 20 taken 636 times.
✓ Branch 20 → 21 taken 271 times.
✓ Branch 20 → 24 taken 365 times.
✓ Branch 22 → 23 taken 262 times.
✓ Branch 22 → 24 taken 9 times.
✓ Branch 25 → 26 taken 262 times.
✓ Branch 25 → 27 taken 374 times.
|
636 | if (!extensionPresent(present, extension) && !missing.contains(extension)) |
| 744 |
1/1✓ Branch 26 → 27 taken 262 times.
|
262 | missing.append(extension); |
| 745 | 558 | } | |
| 746 | // S106-096: only a require block in the leading run (before any command) | ||
| 747 | // gains the missing extensions; RFC 5228 §3.2 forbids using an extension | ||
| 748 | // before its require, so a require further down gets a new line on top. | ||
| 749 | 895 | int patchable = -1; | |
| 750 |
2/2✓ Branch 43 → 36 taken 895 times.
✓ Branch 43 → 44 taken 1 time.
|
896 | for (int i = 0; i < document.blocks.size(); ++i) { |
| 751 | 895 | const BlockKind kind = document.blocks.at(i).kind; | |
| 752 |
2/2✓ Branch 37 → 38 taken 624 times.
✓ Branch 37 → 39 taken 271 times.
|
895 | if (kind == BlockKind::Require) { |
| 753 | 624 | patchable = i; | |
| 754 | 624 | break; | |
| 755 | } | ||
| 756 |
2/2✓ Branch 39 → 40 taken 270 times.
✓ Branch 39 → 41 taken 1 time.
|
271 | if (kind != BlockKind::Comment) |
| 757 | 270 | break; | |
| 758 | } | ||
| 759 | 895 | QByteArray out; | |
| 760 | 895 | bool requirePatched = false; | |
| 761 | 895 | int previousIndex = -2; | |
| 762 | 895 | bool previousDirty = false; | |
| 763 |
2/2✓ Branch 80 → 45 taken 22789 times.
✓ Branch 80 → 81 taken 895 times.
|
23684 | for (int i = 0; i < document.blocks.size(); ++i) { |
| 764 | 22789 | const Block &block = document.blocks.at(i); | |
| 765 |
1/1✓ Branch 46 → 47 taken 22789 times.
|
22789 | QByteArray bytes = blockBytes(block, document.style); |
| 766 |
6/6✓ Branch 47 → 48 taken 624 times.
✓ Branch 47 → 51 taken 22165 times.
✓ Branch 49 → 50 taken 13 times.
✓ Branch 49 → 51 taken 611 times.
✓ Branch 52 → 53 taken 13 times.
✓ Branch 52 → 57 taken 22776 times.
|
22789 | if (i == patchable && !missing.isEmpty()) { |
| 767 |
1/1✓ Branch 53 → 54 taken 13 times.
|
13 | bytes = patchRequire(bytes, missing); |
| 768 | 13 | requirePatched = true; | |
| 769 | } | ||
| 770 |
5/6✓ Branch 57 → 58 taken 22231 times.
✓ Branch 57 → 62 taken 558 times.
✓ Branch 58 → 59 taken 21975 times.
✓ Branch 58 → 62 taken 256 times.
✓ Branch 59 → 60 taken 21975 times.
✗ Branch 59 → 62 not taken.
|
44764 | const bool adjacent = !block.dirty && !previousDirty && block.originalIndex >= 0 && |
| 771 |
2/2✓ Branch 60 → 61 taken 20969 times.
✓ Branch 60 → 62 taken 1006 times.
|
21975 | block.originalIndex == previousIndex + 1; |
| 772 |
9/9✓ Branch 64 → 65 taken 21894 times.
✓ Branch 64 → 69 taken 895 times.
✓ Branch 65 → 66 taken 21894 times.
✓ Branch 66 → 67 taken 5 times.
✓ Branch 66 → 69 taken 21889 times.
✓ Branch 67 → 68 taken 4 times.
✓ Branch 67 → 69 taken 1 time.
✓ Branch 70 → 71 taken 4 times.
✓ Branch 70 → 72 taken 22785 times.
|
22789 | if (!out.isEmpty() && !out.endsWith('\n') && !adjacent) |
| 773 |
1/1✓ Branch 71 → 72 taken 4 times.
|
4 | out += document.style.lineEnd; |
| 774 |
1/1✓ Branch 72 → 73 taken 22789 times.
|
22789 | out += bytes; |
| 775 | 22789 | previousIndex = block.originalIndex; | |
| 776 |
3/4✓ Branch 73 → 74 taken 22231 times.
✓ Branch 73 → 75 taken 558 times.
✗ Branch 74 → 75 not taken.
✓ Branch 74 → 76 taken 22231 times.
|
22789 | previousDirty = block.dirty || block.originalIndex < 0; |
| 777 | 22789 | } | |
| 778 |
6/6✓ Branch 82 → 83 taken 212 times.
✓ Branch 82 → 85 taken 683 times.
✓ Branch 83 → 84 taken 199 times.
✓ Branch 83 → 85 taken 13 times.
✓ Branch 86 → 87 taken 199 times.
✓ Branch 86 → 114 taken 696 times.
|
895 | if (!missing.isEmpty() && !requirePatched) { |
| 779 | 199 | QStringList sorted = missing; | |
| 780 |
1/1✓ Branch 88 → 89 taken 199 times.
|
199 | QByteArray list = "["; |
| 781 |
2/2✓ Branch 103 → 90 taken 242 times.
✓ Branch 103 → 104 taken 199 times.
|
441 | for (int i = 0; i < sorted.size(); ++i) |
| 782 |
6/6✓ Branch 90 → 91 taken 242 times.
✓ Branch 92 → 93 taken 242 times.
✓ Branch 93 → 94 taken 43 times.
✓ Branch 93 → 95 taken 199 times.
✓ Branch 96 → 97 taken 242 times.
✓ Branch 97 → 98 taken 242 times.
|
242 | list += (i > 0 ? "," : "") + stringLiteral(sorted.at(i)); |
| 783 |
4/4✓ Branch 104 → 105 taken 199 times.
✓ Branch 105 → 106 taken 199 times.
✓ Branch 106 → 107 taken 199 times.
✓ Branch 107 → 108 taken 199 times.
|
199 | out.prepend("require " + list + "];" + document.style.lineEnd); |
| 784 | 199 | } | |
| 785 | 895 | return out; | |
| 786 | 895 | } | |
| 787 | |||
| 788 | 234 | int insertRule(Document *document, const Rule &rule) { | |
| 789 | 234 | Block block; | |
| 790 | 234 | block.kind = BlockKind::Rule; | |
| 791 | 234 | block.rule = rule; | |
| 792 | 234 | block.name = rule.name; | |
| 793 | 234 | block.named = !rule.name.isEmpty(); | |
| 794 | 234 | block.dirty = true; | |
| 795 | 234 | int position = document->blocks.size(); | |
| 796 |
6/6✓ Branch 8 → 9 taken 30 times.
✓ Branch 8 → 12 taken 206 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 28 times.
✓ Branch 13 → 7 taken 2 times.
✓ Branch 13 → 14 taken 234 times.
|
236 | while (position > 0 && document->blocks.at(position - 1).kind == BlockKind::Comment) |
| 797 | 2 | --position; | |
| 798 |
2/2✓ Branch 14 → 15 taken 28 times.
✓ Branch 14 → 19 taken 206 times.
|
234 | if (position > 0) { |
| 799 | 28 | const Block &previous = document->blocks.at(position - 1); | |
| 800 |
4/4✓ Branch 16 → 17 taken 5 times.
✓ Branch 16 → 19 taken 23 times.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 19 taken 1 time.
|
28 | if (previous.kind == BlockKind::Advanced && previous.terminal) |
| 801 | 4 | --position; | |
| 802 | } | ||
| 803 |
1/1✓ Branch 19 → 20 taken 234 times.
|
234 | document->blocks.insert(position, block); |
| 804 | 234 | return position; | |
| 805 | 234 | } | |
| 806 | |||
| 807 | 12 | int placeVacation(Document *document, const Vacation &vacation) { | |
| 808 |
2/2✓ Branch 18 → 3 taken 12 times.
✓ Branch 18 → 19 taken 11 times.
|
23 | for (int i = 0; i < document->blocks.size(); ++i) { |
| 809 |
1/1✓ Branch 3 → 4 taken 12 times.
|
12 | Block &block = document->blocks[i]; |
| 810 |
2/2✓ Branch 4 → 5 taken 11 times.
✓ Branch 4 → 6 taken 1 time.
|
12 | if (block.kind != BlockKind::Vacation) |
| 811 | 11 | continue; | |
| 812 | 1 | block.vacation = vacation; | |
| 813 | 1 | block.dirty = true; | |
| 814 |
1/2✗ Branch 7 → 8 not taken.
✓ Branch 7 → 15 taken 1 time.
|
1 | if (!block.named) { |
| 815 | ✗ | block.named = true; | |
| 816 | ✗ | block.name = QStringLiteral("Out of office"); | |
| 817 | } | ||
| 818 | 1 | return i; | |
| 819 | } | ||
| 820 | 11 | Block block; | |
| 821 | 11 | block.kind = BlockKind::Vacation; | |
| 822 | 11 | block.vacation = vacation; | |
| 823 | 11 | block.name = QStringLiteral("Out of office"); | |
| 824 | 11 | block.named = true; | |
| 825 | 11 | block.dirty = true; | |
| 826 | 11 | int position = 0; | |
| 827 |
4/4✓ Branch 29 → 30 taken 6 times.
✓ Branch 29 → 33 taken 8 times.
✓ Branch 34 → 27 taken 3 times.
✓ Branch 34 → 35 taken 11 times.
|
20 | while (position < document->blocks.size() && |
| 828 |
2/2✓ Branch 31 → 32 taken 3 times.
✓ Branch 31 → 33 taken 3 times.
|
6 | document->blocks.at(position).kind == BlockKind::Require) |
| 829 | 3 | ++position; | |
| 830 |
1/1✓ Branch 35 → 36 taken 11 times.
|
11 | document->blocks.insert(position, block); |
| 831 | 11 | return position; | |
| 832 | 11 | } | |
| 833 | |||
| 834 | 7 | int renameMailbox(Document *document, const QString &oldMailbox, const QString &newMailbox, | |
| 835 | QChar delimiter) { | ||
| 836 |
6/8✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 7 taken 1 time.
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 6 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 6 times.
|
7 | if (document->readOnly || oldMailbox.isEmpty() || oldMailbox == newMailbox) |
| 837 | 1 | return 0; | |
| 838 | 6 | int changed = 0; | |
| 839 |
4/4✓ Branch 11 → 12 taken 6 times.
✓ Branch 12 → 13 taken 6 times.
✓ Branch 89 → 14 taken 26 times.
✓ Branch 89 → 90 taken 6 times.
|
32 | for (Block &block : document->blocks) { |
| 840 |
2/2✓ Branch 15 → 16 taken 16 times.
✓ Branch 15 → 35 taken 10 times.
|
26 | if (block.kind == BlockKind::Rule) { |
| 841 | 16 | bool touched = false; | |
| 842 |
4/4✓ Branch 16 → 17 taken 16 times.
✓ Branch 17 → 18 taken 16 times.
✓ Branch 31 → 19 taken 16 times.
✓ Branch 31 → 32 taken 16 times.
|
32 | for (Action &action : block.rule.actions) { |
| 843 | 16 | QString renamed; | |
| 844 |
3/4✓ Branch 20 → 21 taken 16 times.
✗ Branch 20 → 24 not taken.
✓ Branch 25 → 26 taken 8 times.
✓ Branch 25 → 28 taken 8 times.
|
32 | if (action.kind == Action::Kind::FileInto && |
| 845 |
3/3✓ Branch 21 → 22 taken 16 times.
✓ Branch 22 → 23 taken 8 times.
✓ Branch 22 → 24 taken 8 times.
|
16 | renamedTarget(action.target, oldMailbox, newMailbox, delimiter, &renamed)) { |
| 846 | 8 | action.target = renamed; | |
| 847 | 8 | touched = true; | |
| 848 | } | ||
| 849 | 16 | } | |
| 850 |
2/2✓ Branch 32 → 33 taken 8 times.
✓ Branch 32 → 34 taken 8 times.
|
16 | if (touched) { |
| 851 | 8 | block.dirty = true; | |
| 852 | 8 | ++changed; | |
| 853 | } | ||
| 854 | 22 | continue; | |
| 855 | 16 | } | |
| 856 |
5/6✓ Branch 35 → 36 taken 4 times.
✓ Branch 35 → 38 taken 6 times.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 4 times.
✓ Branch 40 → 41 taken 6 times.
✓ Branch 40 → 42 taken 4 times.
|
10 | if (block.kind != BlockKind::Advanced || block.original.isEmpty()) |
| 857 | 6 | continue; | |
| 858 |
1/1✓ Branch 42 → 43 taken 4 times.
|
4 | const Script script = parse(block.original); |
| 859 |
1/2✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 4 times.
|
4 | if (!script.isValid()) |
| 860 | ✗ | continue; | |
| 861 | 4 | QList<StringValue> targets; | |
| 862 |
1/1✓ Branch 46 → 47 taken 4 times.
|
4 | collectFileInto(script.commands, &targets); |
| 863 | 4 | QByteArray patched = block.original; | |
| 864 | 4 | bool touched = false; | |
| 865 |
2/2✓ Branch 75 → 50 taken 4 times.
✓ Branch 75 → 76 taken 4 times.
|
8 | for (int i = targets.size() - 1; i >= 0; --i) { |
| 866 | 4 | QString renamed; | |
| 867 |
2/3✓ Branch 51 → 52 taken 4 times.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 4 times.
|
4 | if (!renamedTarget(targets.at(i).value, oldMailbox, newMailbox, delimiter, &renamed)) |
| 868 | ✗ | continue; | |
| 869 | 4 | const Span span = targets.at(i).span; | |
| 870 |
6/6✓ Branch 55 → 56 taken 4 times.
✓ Branch 56 → 57 taken 4 times.
✓ Branch 57 → 58 taken 4 times.
✓ Branch 58 → 59 taken 4 times.
✓ Branch 59 → 60 taken 4 times.
✓ Branch 60 → 61 taken 4 times.
|
4 | patched = patched.left(span.begin) + stringLiteral(renamed) + patched.mid(span.end); |
| 871 | 4 | touched = true; | |
| 872 | 4 | } | |
| 873 |
1/2✓ Branch 76 → 77 taken 4 times.
✗ Branch 76 → 79 not taken.
|
4 | if (touched) { |
| 874 | // Advanced blocks are patched in place and stay byte-copied. | ||
| 875 | 4 | block.original = patched; | |
| 876 | 4 | ++changed; | |
| 877 | } | ||
| 878 | 4 | } | |
| 879 |
7/7✓ Branch 90 → 91 taken 6 times.
✓ Branch 94 → 95 taken 4 times.
✓ Branch 95 → 96 taken 4 times.
✓ Branch 96 → 97 taken 4 times.
✓ Branch 97 → 98 taken 4 times.
✓ Branch 101 → 92 taken 4 times.
✓ Branch 101 → 102 taken 6 times.
|
10 | qCInfo(lcSieveCodec) << "Mailbox rename touched" << changed << "blocks"; |
| 880 | 6 | return changed; | |
| 881 | } | ||
| 882 | |||
| 883 | } // namespace Sieve | ||
| 884 |