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: 100.0% 65 / 0 / 65
Functions: 100.0% 3 / 0 / 3
Branches: 94.1% 159 / 0 / 169

util/MessageId.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QRegularExpression>
4 #include <QString>
5 #include <QStringList>
6
7 // Bounded RFC 5322 Message-ID tokenization shared by the IMAP parser, cache,
8 // and thread builder. Message-ID local parts are case-sensitive in principle;
9 // only the domain is folded. The parser deliberately does not turn arbitrary
10 // prose containing an @ into an identity.
11 namespace MessageId {
12
13 inline constexpr qsizetype MaximumFieldCharacters = 64 * 1024;
14 inline constexpr qsizetype MaximumTokenCharacters = 998;
15 inline constexpr qsizetype MaximumTokens = 100;
16
17 4065218 inline QString canonicalize(QString token) {
18
1/1
✓ Branch 2 → 3 taken 4065218 times.
4065218 token = token.trimmed();
19
1/1
✓ Branch 7 → 8 taken 4065218 times.
4065218 if (token.startsWith(QLatin1Char('<')) &&
20
8/9
✓ Branch 8 → 9 taken 35427 times.
✓ Branch 8 → 16 taken 4029791 times.
✓ Branch 11 → 12 taken 35427 times.
✓ Branch 12 → 13 taken 34485 times.
✓ Branch 12 → 16 taken 942 times.
✓ Branch 14 → 15 taken 34485 times.
✗ Branch 14 → 16 not taken.
✓ Branch 17 → 18 taken 34485 times.
✓ Branch 17 → 25 taken 4030733 times.
4065218 token.endsWith(QLatin1Char('>')) && token.size() >= 2) {
21
2/2
✓ Branch 19 → 20 taken 34485 times.
✓ Branch 20 → 21 taken 34485 times.
34485 token = token.mid(1, token.size() - 2).trimmed();
22 }
23
2/2
✓ Branch 28 → 29 taken 3090668 times.
✓ Branch 28 → 48 taken 3 times.
7155889 if (token.isEmpty() || token.size() > MaximumTokenCharacters ||
24
6/6
✓ Branch 31 → 32 taken 3090668 times.
✓ Branch 32 → 33 taken 3086779 times.
✓ Branch 32 → 48 taken 3889 times.
✓ Branch 35 → 36 taken 3086779 times.
✓ Branch 36 → 37 taken 3084490 times.
✓ Branch 36 → 48 taken 2289 times.
3090668 token.contains(QLatin1Char('<')) || token.contains(QLatin1Char('>')) ||
25
8/8
✓ Branch 26 → 27 taken 3090671 times.
✓ Branch 26 → 48 taken 974547 times.
✓ Branch 39 → 40 taken 3084490 times.
✓ Branch 40 → 41 taken 3083803 times.
✓ Branch 40 → 48 taken 687 times.
✓ Branch 43 → 44 taken 3083803 times.
✓ Branch 44 → 45 taken 3083318 times.
✓ Branch 44 → 48 taken 485 times.
10239207 token.contains(QLatin1Char('\r')) || token.contains(QLatin1Char('\n')) ||
26
5/5
✓ Branch 46 → 47 taken 3083318 times.
✓ Branch 47 → 48 taken 1 time.
✓ Branch 47 → 49 taken 3083317 times.
✓ Branch 50 → 51 taken 981901 times.
✓ Branch 50 → 52 taken 3083317 times.
7148536 token.contains(QChar::Null)) {
27 981901 return {};
28 }
29
30 // The last @ is the domain separator even for an obsolete quoted local
31 // part containing @. CFWS immediately around that separator is syntax-only.
32 3083317 const qsizetype at = token.lastIndexOf(QLatin1Char('@'));
33
6/6
✓ Branch 55 → 56 taken 3080781 times.
✓ Branch 55 → 58 taken 2536 times.
✓ Branch 57 → 58 taken 295 times.
✓ Branch 57 → 59 taken 3080486 times.
✓ Branch 60 → 61 taken 2831 times.
✓ Branch 60 → 62 taken 3080486 times.
3083317 if (at <= 0 || at >= token.size() - 1)
34 2831 return {};
35
2/2
✓ Branch 62 → 63 taken 3080486 times.
✓ Branch 63 → 64 taken 3080486 times.
3080486 QString local = token.left(at).trimmed();
36
2/2
✓ Branch 65 → 66 taken 3080486 times.
✓ Branch 66 → 67 taken 3080486 times.
3080486 QString domain = token.mid(at + 1).trimmed();
37 3080486 const bool quotedLocal = local.size() >= 2 &&
38
5/5
✓ Branch 69 → 70 taken 3072509 times.
✓ Branch 69 → 79 taken 7977 times.
✓ Branch 72 → 73 taken 3072509 times.
✓ Branch 73 → 74 taken 34 times.
✓ Branch 73 → 79 taken 3072475 times.
3080520 local.startsWith(QLatin1Char('"')) &&
39
3/3
✓ Branch 76 → 77 taken 34 times.
✓ Branch 77 → 78 taken 12 times.
✓ Branch 77 → 79 taken 22 times.
34 local.endsWith(QLatin1Char('"'));
40
4/5
✓ Branch 83 → 84 taken 3080486 times.
✗ Branch 83 → 103 not taken.
✓ Branch 85 → 86 taken 3080486 times.
✓ Branch 86 → 87 taken 3080463 times.
✓ Branch 86 → 103 taken 23 times.
6160972 if (local.isEmpty() || domain.isEmpty() || domain.contains(QChar::Space) ||
41
6/8
✓ Branch 81 → 82 taken 3080486 times.
✗ Branch 81 → 103 not taken.
✓ Branch 89 → 90 taken 3080463 times.
✓ Branch 90 → 91 taken 3080462 times.
✓ Branch 90 → 103 taken 1 time.
✓ Branch 93 → 94 taken 3080462 times.
✓ Branch 94 → 95 taken 3080462 times.
✗ Branch 94 → 103 not taken.
9241434 domain.contains(QLatin1Char('\t')) || domain.contains(QLatin1Char('@')) ||
42
2/2
✓ Branch 95 → 96 taken 3080450 times.
✓ Branch 95 → 104 taken 12 times.
3080462 (!quotedLocal &&
43
8/8
✓ Branch 97 → 98 taken 3080450 times.
✓ Branch 98 → 99 taken 3080421 times.
✓ Branch 98 → 103 taken 29 times.
✓ Branch 101 → 102 taken 3080421 times.
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 104 taken 3080420 times.
✓ Branch 105 → 106 taken 54 times.
✓ Branch 105 → 107 taken 3080432 times.
6160936 (local.contains(QChar::Space) || local.contains(QLatin1Char('\t')))))
44 54 return {};
45
4/4
✓ Branch 107 → 108 taken 3080432 times.
✓ Branch 108 → 109 taken 3080432 times.
✓ Branch 115 → 110 taken 59708606 times.
✓ Branch 115 → 116 taken 3080431 times.
62789037 for (const QChar ch : token) {
46
2/2
✓ Branch 111 → 112 taken 1 time.
✓ Branch 111 → 114 taken 59708605 times.
59708606 if (ch.category() == QChar::Other_Control)
47 1 return {};
48 }
49
3/3
✓ Branch 116 → 117 taken 3080431 times.
✓ Branch 119 → 120 taken 3080431 times.
✓ Branch 120 → 121 taken 3080431 times.
3080431 return local + QLatin1Char('@') + domain.toLower();
50 3080486 }
51
52 2512738 inline QStringList parseTokens(const QString &field) {
53
1/1
✓ Branch 2 → 3 taken 2512738 times.
2512738 const QString bounded = field.left(MaximumFieldCharacters);
54 2512738 QStringList result;
55
1/1
✓ Branch 4 → 5 taken 2512738 times.
2512738 result.reserve(qMin<qsizetype>(MaximumTokens, 16));
56
57 static const QRegularExpression bracketed(
58
4/7
✓ Branch 5 → 6 taken 25 times.
✓ Branch 5 → 18 taken 2512713 times.
✓ Branch 7 → 8 taken 25 times.
✗ Branch 7 → 18 not taken.
✓ Branch 12 → 13 taken 25 times.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
2513349 QStringLiteral(R"(<\s*([^<>\r\n]{1,998}?)\s*>)"));
59
1/1
✓ Branch 19 → 20 taken 2512738 times.
2512738 auto matches = bracketed.globalMatch(bounded);
60
7/7
✓ Branch 31 → 32 taken 2513543 times.
✓ Branch 32 → 33 taken 806 times.
✓ Branch 32 → 36 taken 2512737 times.
✓ Branch 34 → 35 taken 805 times.
✓ Branch 34 → 36 taken 1 time.
✓ Branch 37 → 21 taken 805 times.
✓ Branch 37 → 38 taken 2512738 times.
2513543 while (matches.hasNext() && result.size() < MaximumTokens) {
61
3/3
✓ Branch 21 → 22 taken 805 times.
✓ Branch 22 → 23 taken 805 times.
✓ Branch 23 → 24 taken 805 times.
805 const QString token = canonicalize(matches.next().captured(1));
62
2/2
✓ Branch 27 → 28 taken 223 times.
✓ Branch 27 → 29 taken 582 times.
805 if (!token.isEmpty())
63
1/1
✓ Branch 28 → 29 taken 223 times.
223 result.append(token);
64 805 }
65
2/2
✓ Branch 39 → 40 taken 107 times.
✓ Branch 39 → 41 taken 2512631 times.
2512738 if (!result.isEmpty())
66 107 return result;
67
68 // ENVELOPE implementations sometimes return a bare msg-id. Accept it only
69 // when the complete field, apart from surrounding comments/CFWS, is one ID.
70 2512631 QString bare = bounded;
71
4/7
✓ Branch 42 → 43 taken 23 times.
✓ Branch 42 → 55 taken 2512608 times.
✓ Branch 44 → 45 taken 23 times.
✗ Branch 44 → 55 not taken.
✓ Branch 49 → 50 taken 23 times.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 108 not taken.
2512654 static const QRegularExpression comments(QStringLiteral(R"(\([^()]*\))"));
72
7/7
✓ Branch 58 → 59 taken 2536355 times.
✓ Branch 58 → 64 taken 3266 times.
✓ Branch 61 → 62 taken 2536355 times.
✓ Branch 62 → 63 taken 26990 times.
✓ Branch 62 → 64 taken 2509365 times.
✓ Branch 65 → 56 taken 26990 times.
✓ Branch 65 → 66 taken 2512631 times.
2539621 for (int pass = 0; pass < 8 && bare.contains(QLatin1Char('(')); ++pass)
73
1/1
✓ Branch 56 → 57 taken 26990 times.
26990 bare.remove(comments);
74
1/1
✓ Branch 67 → 68 taken 2512631 times.
2512631 const QString token = canonicalize(bare);
75
2/2
✓ Branch 70 → 71 taken 1724622 times.
✓ Branch 70 → 72 taken 788009 times.
2512631 if (!token.isEmpty())
76
1/1
✓ Branch 71 → 72 taken 1724622 times.
1724622 result.append(token);
77 2512631 return result;
78 2512738 }
79
80 1555666 inline QString canonicalFirst(const QString &field) {
81 // Fast paths for the two shapes almost every header has — a bare id or
82 // one bracketed id — with the result parseTokens() would give: no
83 // comments to strip, nothing for the bracket scan to find besides the
84 // whole field (Sprint 101 S101.0.6: the regex dominated the summary pass).
85
1/1
✓ Branch 2 → 3 taken 1555666 times.
1555666 const QString bounded = field.left(MaximumFieldCharacters);
86
4/4
✓ Branch 5 → 6 taken 1555666 times.
✓ Branch 9 → 10 taken 1553603 times.
✓ Branch 10 → 11 taken 1518040 times.
✓ Branch 10 → 16 taken 35563 times.
3109269 if (!bounded.contains(QLatin1Char('(')) && !bounded.contains(QLatin1Char('>'))
87
7/7
✓ Branch 6 → 7 taken 1553603 times.
✓ Branch 6 → 16 taken 2063 times.
✓ Branch 13 → 14 taken 1518040 times.
✓ Branch 14 → 15 taken 1517351 times.
✓ Branch 14 → 16 taken 689 times.
✓ Branch 17 → 18 taken 1517351 times.
✓ Branch 17 → 23 taken 38315 times.
3109269 && !bounded.contains(QLatin1Char('<')))
88
1/1
✓ Branch 19 → 20 taken 1517351 times.
1517351 return canonicalize(bounded);
89
1/1
✓ Branch 23 → 24 taken 38315 times.
38315 const QString trimmed = bounded.trimmed();
90
4/4
✓ Branch 26 → 27 taken 38315 times.
✓ Branch 30 → 31 taken 34907 times.
✓ Branch 31 → 32 taken 34444 times.
✓ Branch 31 → 56 taken 463 times.
73222 if (trimmed.startsWith(QLatin1Char('<')) && trimmed.endsWith(QLatin1Char('>'))
91
4/4
✓ Branch 35 → 36 taken 34425 times.
✓ Branch 35 → 56 taken 19 times.
✓ Branch 40 → 41 taken 34419 times.
✓ Branch 40 → 56 taken 6 times.
34444 && trimmed.indexOf(QLatin1Char('<'), 1) < 0 && trimmed.indexOf(QLatin1Char('>')) == trimmed.size() - 1
92
6/6
✓ Branch 43 → 44 taken 34419 times.
✓ Branch 44 → 45 taken 34412 times.
✓ Branch 44 → 56 taken 7 times.
✓ Branch 47 → 48 taken 34412 times.
✓ Branch 48 → 49 taken 34409 times.
✓ Branch 48 → 56 taken 3 times.
34419 && !trimmed.contains(QLatin1Char('(')) && !trimmed.contains(QLatin1Char('\r'))
93
9/9
✓ Branch 27 → 28 taken 34907 times.
✓ Branch 27 → 56 taken 3408 times.
✓ Branch 51 → 52 taken 34409 times.
✓ Branch 52 → 53 taken 34408 times.
✓ Branch 52 → 56 taken 1 time.
✓ Branch 54 → 55 taken 34406 times.
✓ Branch 54 → 56 taken 2 times.
✓ Branch 57 → 58 taken 34406 times.
✓ Branch 57 → 63 taken 3909 times.
73222 && !trimmed.contains(QLatin1Char('\n')) && trimmed.size() <= MaximumTokenCharacters + 2)
94
1/1
✓ Branch 59 → 60 taken 34406 times.
34406 return canonicalize(trimmed);
95
1/1
✓ Branch 63 → 64 taken 3909 times.
3909 const QStringList tokens = parseTokens(field);
96
2/2
✓ Branch 65 → 66 taken 3836 times.
✓ Branch 65 → 67 taken 73 times.
3909 return tokens.isEmpty() ? QString() : tokens.first();
97 1555666 }
98
99 } // namespace MessageId
100