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: 93.6% 247 / 0 / 264
Functions: 96.4% 27 / 0 / 28
Branches: 86.7% 379 / 0 / 437

presentation/RecipientChipModel.cpp
Line Branch Exec Source
1 #include "RecipientChipModel.h"
2
3 #include "ComposeRecipients.h"
4
5 #include <QRegularExpression>
6
7 namespace {
8
9 // Name and address of one parsed mailbox header ("Name" <a@b> | a@b).
10 839 QPair<QString, QString> splitMailbox(const QString &header) {
11 839 qsizetype left = -1;
12 839 bool quoted = false;
13
2/2
✓ Branch 21 → 3 taken 20210 times.
✓ Branch 21 → 22 taken 839 times.
21049 for (qsizetype i = 0; i < header.size(); ++i) {
14
2/2
✓ Branch 7 → 8 taken 26 times.
✓ Branch 7 → 9 taken 20184 times.
20210 if (header.at(i) == QLatin1Char('"')) quoted = !quoted;
15
6/6
✓ Branch 9 → 10 taken 20074 times.
✓ Branch 9 → 16 taken 136 times.
✓ Branch 14 → 15 taken 427 times.
✓ Branch 14 → 16 taken 19647 times.
✓ Branch 17 → 18 taken 427 times.
✓ Branch 17 → 19 taken 19783 times.
20210 if (!quoted && header.at(i) == QLatin1Char('<')) left = i;
16 }
17
3/3
✓ Branch 22 → 23 taken 412 times.
✓ Branch 22 → 29 taken 427 times.
✓ Branch 24 → 25 taken 412 times.
839 if (left < 0) return {QString(), header.trimmed()};
18
2/2
✓ Branch 29 → 30 taken 427 times.
✓ Branch 30 → 31 taken 427 times.
427 QString name = header.left(left).trimmed();
19
8/10
✓ Branch 33 → 34 taken 427 times.
✗ Branch 33 → 43 not taken.
✓ Branch 36 → 37 taken 427 times.
✓ Branch 37 → 38 taken 13 times.
✓ Branch 37 → 43 taken 414 times.
✓ Branch 40 → 41 taken 13 times.
✓ Branch 41 → 42 taken 13 times.
✗ Branch 41 → 43 not taken.
✓ Branch 44 → 45 taken 13 times.
✓ Branch 44 → 72 taken 414 times.
427 if (name.size() >= 2 && name.startsWith(QLatin1Char('"')) && name.endsWith(QLatin1Char('"'))) {
20
1/1
✓ Branch 46 → 47 taken 13 times.
13 name = name.mid(1, name.size() - 2);
21
1/1
✓ Branch 55 → 56 taken 13 times.
26 name.replace(QStringLiteral("\\\""), QStringLiteral("\""));
22
1/1
✓ Branch 66 → 67 taken 13 times.
26 name.replace(QStringLiteral("\\\\"), QStringLiteral("\\"));
23 }
24 427 const qsizetype right = header.indexOf(QLatin1Char('>'), left);
25
2/2
✓ Branch 75 → 76 taken 427 times.
✓ Branch 76 → 77 taken 427 times.
427 return {name, header.mid(left + 1, right - left - 1).trimmed()};
26 427 }
27
28 207 QString initialsOf(const QString &name, const QString &address) {
29
3/3
✓ Branch 3 → 4 taken 116 times.
✓ Branch 3 → 8 taken 91 times.
✓ Branch 7 → 9 taken 116 times.
323 const QString source = name.isEmpty() ? address.section(QLatin1Char('@'), 0, 0) : name;
30 207 QString initials;
31
6/6
✓ Branch 14 → 15 taken 207 times.
✓ Branch 15 → 16 taken 207 times.
✓ Branch 19 → 20 taken 207 times.
✓ Branch 20 → 21 taken 207 times.
✓ Branch 35 → 22 taken 295 times.
✓ Branch 35 → 36 taken 121 times.
623 for (const QString &part : source.split(QRegularExpression(QStringLiteral("[\\s._-]+")), Qt::SkipEmptyParts)) {
32
3/3
✓ Branch 23 → 24 taken 295 times.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 27 taken 292 times.
295 if (!part.front().isLetterOrNumber()) continue;
33
2/2
✓ Branch 27 → 28 taken 292 times.
✓ Branch 29 → 30 taken 292 times.
292 initials += part.front().toUpper();
34
2/2
✓ Branch 31 → 32 taken 86 times.
✓ Branch 31 → 33 taken 206 times.
292 if (initials.size() == 2) break;
35 207 }
36
4/4
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 42 taken 206 times.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 206 times.
415 return initials.isEmpty() ? QStringLiteral("?") : initials;
37 207 }
38
39 // Splits at top-level separators (outside quotes and angle brackets).
40 885 QStringList topLevelParts(const QString &text) {
41 885 QStringList parts;
42 885 QString current;
43 885 bool quoted = false, angle = false, escaped = false;
44
2/2
✓ Branch 61 → 4 taken 5019 times.
✓ Branch 61 → 62 taken 885 times.
5904 for (const QChar c : text) {
45
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 5019 times.
5019 if (escaped) {
46 ✗ escaped = false;
47 ✗ current.append(c);
48 ✗ continue;
49 }
50
4/6
✓ Branch 7 → 8 taken 61 times.
✓ Branch 7 → 13 taken 4958 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 61 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 5019 times.
5019 if (quoted && c == QLatin1Char('\\')) escaped = true;
51
2/2
✓ Branch 19 → 20 taken 7 times.
✓ Branch 19 → 21 taken 5012 times.
5019 else if (c == QLatin1Char('"')) quoted = !quoted;
52
6/6
✓ Branch 21 → 22 taken 4954 times.
✓ Branch 21 → 27 taken 58 times.
✓ Branch 25 → 26 taken 86 times.
✓ Branch 25 → 27 taken 4868 times.
✓ Branch 28 → 29 taken 86 times.
✓ Branch 28 → 30 taken 4926 times.
5012 else if (!quoted && c == QLatin1Char('<')) angle = true;
53
6/6
✓ Branch 30 → 31 taken 4868 times.
✓ Branch 30 → 36 taken 58 times.
✓ Branch 34 → 35 taken 86 times.
✓ Branch 34 → 36 taken 4782 times.
✓ Branch 37 → 38 taken 86 times.
✓ Branch 37 → 39 taken 4840 times.
4926 else if (!quoted && c == QLatin1Char('>')) angle = false;
54
12/12
✓ Branch 39 → 40 taken 4782 times.
✓ Branch 39 → 54 taken 58 times.
✓ Branch 40 → 41 taken 3194 times.
✓ Branch 40 → 54 taken 1588 times.
✓ Branch 44 → 45 taken 3151 times.
✓ Branch 44 → 53 taken 43 times.
✓ Branch 48 → 49 taken 3149 times.
✓ Branch 48 → 53 taken 2 times.
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 54 taken 3148 times.
✓ Branch 55 → 56 taken 46 times.
✓ Branch 55 → 59 taken 4794 times.
4840 else if (!quoted && !angle && (c == QLatin1Char(',') || c == QLatin1Char(';') || c == QLatin1Char('\n'))) {
55
1/1
✓ Branch 56 → 57 taken 46 times.
46 parts.append(current);
56 46 current.clear();
57 46 continue;
58 }
59
1/1
✓ Branch 59 → 60 taken 4973 times.
4973 current.append(c);
60 }
61
1/1
✓ Branch 62 → 63 taken 885 times.
885 parts.append(current);
62 885 return parts;
63 885 }
64
65 } // namespace
66
67 707 RecipientChipModel::RecipientChipModel(const QString &fieldName, QObject *parent)
68 707 : QAbstractListModel(parent), m_fieldName(fieldName) {}
69
70 3250 int RecipientChipModel::rowCount(const QModelIndex &parent) const {
71
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3250 times.
3250 return parent.isValid() ? 0 : int(m_chips.size());
72 }
73
74 1862 QVariant RecipientChipModel::data(const QModelIndex &index, int role) const {
75
3/6
✓ Branch 3 → 4 taken 1862 times.
✗ Branch 3 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1862 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 1862 times.
1862 if (!index.isValid() || index.row() >= m_chips.size()) return {};
76 1862 const Chip &chip = m_chips.at(index.row());
77
9/10
✓ Branch 13 → 14 taken 207 times.
✓ Branch 13 → 37 taken 207 times.
✓ Branch 13 → 38 taken 206 times.
✓ Branch 13 → 39 taken 209 times.
✓ Branch 13 → 40 taken 207 times.
✓ Branch 13 → 44 taken 206 times.
✓ Branch 13 → 48 taken 207 times.
✓ Branch 13 → 50 taken 206 times.
✓ Branch 13 → 79 taken 207 times.
✗ Branch 13 → 132 not taken.
1862 switch (role) {
78 207 case Qt::DisplayRole:
79 case DisplayRole:
80
5/5
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 28 taken 205 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
211 if (chip.state == QLatin1String("group")) return tr("%1 (%2)").arg(chip.name).arg(chip.members.size());
81
2/2
✓ Branch 30 → 31 taken 19 times.
✓ Branch 30 → 32 taken 186 times.
205 if (chip.state == QLatin1String("invalid")) return chip.raw;
82
2/2
✓ Branch 33 → 34 taken 97 times.
✓ Branch 33 → 35 taken 89 times.
186 return chip.name.isEmpty() ? chip.address : chip.name;
83 207 case NameRole: return chip.name;
84 206 case AddressRole: return chip.address;
85 209 case StateRole: return chip.state;
86
1/1
✓ Branch 40 → 41 taken 207 times.
207 case InitialsRole: return initialsOf(chip.name, chip.address);
87
1/1
✓ Branch 44 → 45 taken 206 times.
206 case ColorIndexRole: return int(qHash(chip.address.toLower()) % 8);
88 207 case MemberCountRole: return int(chip.members.size());
89 206 case TooltipRole:
90
3/3
✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 62 taken 205 times.
✓ Branch 56 → 57 taken 1 time.
207 if (chip.state == QLatin1String("group")) return chip.members.join(QStringLiteral(", "));
91
4/4
✓ Branch 64 → 65 taken 19 times.
✓ Branch 64 → 72 taken 186 times.
✓ Branch 65 → 66 taken 19 times.
✓ Branch 67 → 68 taken 19 times.
224 if (chip.state == QLatin1String("invalid")) return tr("%1 is not a valid address").arg(chip.raw);
92
3/3
✓ Branch 73 → 74 taken 97 times.
✓ Branch 73 → 75 taken 89 times.
✓ Branch 75 → 76 taken 89 times.
186 return chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address);
93 207 case AccessibleRole: {
94
4/4
✓ Branch 79 → 80 taken 207 times.
✓ Branch 82 → 83 taken 207 times.
✓ Branch 85 → 86 taken 207 times.
✓ Branch 87 → 88 taken 207 times.
828 const QString position = tr("recipient %1 of %2 in %3").arg(index.row() + 1).arg(m_chips.size()).arg(m_fieldName);
95
2/2
✓ Branch 93 → 94 taken 19 times.
✓ Branch 93 → 100 taken 188 times.
207 if (chip.state == QLatin1String("invalid"))
96
2/2
✓ Branch 94 → 95 taken 19 times.
✓ Branch 95 → 96 taken 19 times.
19 return tr("%1, not a valid address, %2").arg(chip.raw, position);
97
2/2
✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 117 taken 187 times.
188 if (chip.state == QLatin1String("group"))
98
4/4
✓ Branch 103 → 104 taken 1 time.
✓ Branch 105 → 106 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
✓ Branch 110 → 111 taken 1 time.
4 return tr("Group %1 with %2 people, %3").arg(chip.name).arg(chip.members.size()).arg(position);
99
4/6
✓ Branch 118 → 119 taken 97 times.
✓ Branch 118 → 121 taken 90 times.
✓ Branch 119 → 120 taken 97 times.
✓ Branch 120 → 123 taken 97 times.
✗ Branch 191 → 192 not taken.
✗ Branch 191 → 193 not taken.
561 return chip.name.isEmpty() ? tr("%1, %2").arg(chip.address, position)
100
6/8
✓ Branch 121 → 122 taken 90 times.
✓ Branch 122 → 123 taken 90 times.
✓ Branch 125 → 126 taken 90 times.
✓ Branch 125 → 127 taken 97 times.
✓ Branch 127 → 128 taken 97 times.
✓ Branch 127 → 129 taken 90 times.
✗ Branch 187 → 188 not taken.
✗ Branch 187 → 189 not taken.
374 : tr("%1, %2, %3").arg(chip.name, chip.address, position);
101 207 }
102 ✗ default: return {};
103 }
104 }
105
106 413 QHash<int, QByteArray> RecipientChipModel::roleNames() const {
107 ✗ return {{DisplayRole, "display"}, {NameRole, "name"}, {AddressRole, "address"},
108 ✗ {StateRole, "state"}, {InitialsRole, "initials"}, {ColorIndexRole, "colorIndex"},
109
2/4
✓ Branch 13 → 14 taken 3717 times.
✓ Branch 13 → 15 taken 413 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
4130 {MemberCountRole, "memberCount"}, {TooltipRole, "tooltip"}, {AccessibleRole, "accessibleName"}};
110 413 }
111
112 14 bool RecipientChipModel::hasInvalid() const {
113
2/2
✓ Branch 11 → 4 taken 12 times.
✓ Branch 11 → 12 taken 4 times.
16 for (const Chip &chip : m_chips)
114
2/2
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 9 taken 2 times.
12 if (chip.state == QLatin1String("invalid")) return true;
115 4 return false;
116 }
117
118 2096 QStringList RecipientChipModel::headers() const {
119 2096 QStringList out;
120
2/2
✓ Branch 22 → 4 taken 946 times.
✓ Branch 22 → 23 taken 2096 times.
3042 for (const Chip &chip : m_chips) {
121
2/2
✓ Branch 7 → 8 taken 33 times.
✓ Branch 7 → 9 taken 913 times.
946 if (chip.state == QLatin1String("invalid")) continue;
122
3/3
✓ Branch 11 → 12 taken 5 times.
✓ Branch 11 → 13 taken 908 times.
✓ Branch 12 → 20 taken 5 times.
913 if (chip.state == QLatin1String("group")) out.append(chip.members);
123
4/4
✓ Branch 14 → 15 taken 434 times.
✓ Branch 14 → 16 taken 474 times.
✓ Branch 16 → 17 taken 474 times.
✓ Branch 17 → 18 taken 908 times.
908 else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address));
124 }
125 2096 return out;
126 ✗ }
127
128 3339 QString RecipientChipModel::draftText() const {
129 3339 QStringList out;
130
2/2
✓ Branch 22 → 4 taken 951 times.
✓ Branch 22 → 23 taken 3339 times.
4290 for (const Chip &chip : m_chips) {
131
3/3
✓ Branch 7 → 8 taken 103 times.
✓ Branch 7 → 9 taken 848 times.
✓ Branch 8 → 20 taken 103 times.
951 if (chip.state == QLatin1String("invalid")) out.append(chip.raw);
132
3/3
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 847 times.
✓ Branch 12 → 20 taken 1 time.
848 else if (chip.state == QLatin1String("group")) out.append(chip.members);
133
4/4
✓ Branch 14 → 15 taken 494 times.
✓ Branch 14 → 16 taken 353 times.
✓ Branch 16 → 17 taken 353 times.
✓ Branch 17 → 18 taken 847 times.
847 else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address));
134 }
135
1/1
✓ Branch 26 → 27 taken 3339 times.
6678 return out.join(QStringLiteral(", "));
136 3339 }
137
138 1354 QStringList RecipientChipModel::addresses() const {
139 1354 QStringList out;
140
7/7
✓ Branch 2 → 3 taken 1354 times.
✓ Branch 3 → 4 taken 1354 times.
✓ Branch 4 → 5 taken 1354 times.
✓ Branch 7 → 8 taken 607 times.
✓ Branch 8 → 9 taken 607 times.
✓ Branch 12 → 6 taken 607 times.
✓ Branch 12 → 13 taken 1354 times.
1961 for (const QString &header : headers()) out.append(splitMailbox(header).second);
141 1354 return out;
142 ✗ }
143
144 142 QStringList RecipientChipModel::invalidTexts() const {
145 142 QStringList out;
146
2/2
✓ Branch 11 → 4 taken 77 times.
✓ Branch 11 → 12 taken 142 times.
219 for (const Chip &chip : m_chips)
147
3/3
✓ Branch 7 → 8 taken 16 times.
✓ Branch 7 → 9 taken 61 times.
✓ Branch 8 → 9 taken 16 times.
77 if (chip.state == QLatin1String("invalid")) out.append(chip.raw);
148 142 return out;
149 ✗ }
150
151
2/2
✓ Branch 2 → 3 taken 292 times.
✓ Branch 6 → 7 taken 292 times.
584 QString RecipientChipModel::text() const { return headers().join(QStringLiteral(", ")); }
152
153 // A chip that differs only in letter case is the same person to every
154 // mail provider in practice, so the field shows it once (§4.5). The
155 // envelope keeps RFC 5321's case-sensitive local part (ComposeRecipients).
156 232 bool RecipientChipModel::containsAddress(const QString &address) const {
157
1/1
✓ Branch 2 → 3 taken 232 times.
232 const QString key = address.toCaseFolded();
158
5/5
✓ Branch 3 → 4 taken 232 times.
✓ Branch 4 → 5 taken 232 times.
✓ Branch 5 → 6 taken 232 times.
✓ Branch 15 → 7 taken 56 times.
✓ Branch 15 → 16 taken 230 times.
286 for (const QString &existing : addresses())
159
5/5
✓ Branch 8 → 9 taken 56 times.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 54 times.
✓ Branch 18 → 19 taken 230 times.
✓ Branch 18 → 21 taken 2 times.
286 if (existing.toCaseFolded() == key) return true;
160 230 return false;
161 232 }
162
163 244 RecipientChipModel::Chip RecipientChipModel::chipFor(const QString &mailbox) const {
164 244 Chip chip;
165
1/1
✓ Branch 2 → 3 taken 244 times.
244 const auto parsed = ComposeRecipients::parse(mailbox);
166
5/6
✓ Branch 4 → 5 taken 226 times.
✓ Branch 4 → 8 taken 18 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 226 times.
✓ Branch 10 → 11 taken 18 times.
✓ Branch 10 → 22 taken 226 times.
244 if (!parsed || parsed->headers.size() != 1) {
167 18 chip.state = QStringLiteral("invalid");
168
1/1
✓ Branch 17 → 18 taken 18 times.
18 chip.raw = mailbox.trimmed();
169 18 chip.address = chip.raw;
170 18 return chip;
171 }
172
1/1
✓ Branch 24 → 25 taken 226 times.
226 const auto [name, address] = splitMailbox(parsed->headers.first());
173 226 chip.name = name;
174 226 chip.address = address;
175
12/18
✓ Branch 30 → 31 taken 190 times.
✓ Branch 30 → 37 taken 36 times.
✓ Branch 31 → 32 taken 190 times.
✓ Branch 32 → 33 taken 190 times.
✓ Branch 33 → 34 taken 1 time.
✓ Branch 33 → 37 taken 189 times.
✓ Branch 42 → 43 taken 225 times.
✓ Branch 42 → 44 taken 1 time.
✓ Branch 44 → 45 taken 1 time.
✓ Branch 44 → 46 taken 225 times.
✓ Branch 46 → 47 taken 190 times.
✓ Branch 46 → 48 taken 36 times.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 55 not taken.
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 63 not taken.
452 chip.state = m_known && m_known(address.toLower()) ? QStringLiteral("known") : QStringLiteral("new");
176 226 return chip;
177 244 }
178
179 238 void RecipientChipModel::append(const Chip &chip) {
180
9/9
✓ Branch 4 → 5 taken 221 times.
✓ Branch 4 → 11 taken 17 times.
✓ Branch 7 → 8 taken 219 times.
✓ Branch 7 → 11 taken 2 times.
✓ Branch 8 → 9 taken 219 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 218 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 15 taken 237 times.
238 if (chip.state != QLatin1String("invalid") && chip.state != QLatin1String("group") && containsAddress(chip.address)) {
181 1 emit duplicateSkipped(chip.address);
182 1 return;
183 }
184
1/1
✓ Branch 18 → 19 taken 237 times.
237 beginInsertRows({}, int(m_chips.size()), int(m_chips.size()));
185 237 m_chips.append(chip);
186 237 endInsertRows();
187 }
188
189 723 void RecipientChipModel::setText(const QString &text) {
190 723 beginResetModel();
191 723 m_chips.clear();
192 723 endResetModel();
193
5/5
✓ Branch 5 → 6 taken 723 times.
✓ Branch 6 → 7 taken 723 times.
✓ Branch 7 → 8 taken 723 times.
✓ Branch 21 → 9 taken 768 times.
✓ Branch 21 → 22 taken 723 times.
1491 for (const QString &part : topLevelParts(text)) {
194
3/3
✓ Branch 10 → 11 taken 768 times.
✓ Branch 13 → 14 taken 548 times.
✓ Branch 13 → 15 taken 220 times.
768 if (part.trimmed().isEmpty()) continue;
195
2/2
✓ Branch 15 → 16 taken 220 times.
✓ Branch 16 → 17 taken 220 times.
220 append(chipFor(part));
196 723 }
197 723 emit changed();
198 723 }
199
200 162 QString RecipientChipModel::commit(const QString &text, bool force) {
201
1/1
✓ Branch 2 → 3 taken 162 times.
162 const QStringList parts = topLevelParts(text);
202 162 QStringList rest;
203
2/2
✓ Branch 32 → 4 taken 163 times.
✓ Branch 32 → 33 taken 162 times.
325 for (qsizetype i = 0; i < parts.size(); ++i) {
204
1/1
✓ Branch 5 → 6 taken 163 times.
163 const QString part = parts.at(i).trimmed();
205
2/2
✓ Branch 7 → 8 taken 154 times.
✓ Branch 7 → 9 taken 9 times.
163 if (part.isEmpty()) continue;
206
1/1
✓ Branch 9 → 10 taken 9 times.
9 const Chip chip = chipFor(part);
207 // The last part is still being typed unless the commit is forced.
208
6/6
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 15 taken 5 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 3 times.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 19 taken 8 times.
9 if (chip.state == QLatin1String("invalid") && !force) {
209
1/1
✓ Branch 17 → 18 taken 1 time.
1 rest.append(part);
210 1 continue;
211 }
212
1/1
✓ Branch 19 → 20 taken 8 times.
8 append(chip);
213 164 }
214
1/1
✓ Branch 33 → 34 taken 162 times.
162 emit changed();
215
1/1
✓ Branch 37 → 38 taken 162 times.
324 return rest.join(QStringLiteral(", "));
216 162 }
217
218 3 bool RecipientChipModel::commitsOnSeparator(const QString &text) const {
219
1/1
✓ Branch 2 → 3 taken 3 times.
3 const QString trimmed = text.trimmed();
220
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3 times.
3 if (trimmed.isEmpty()) return false;
221
1/1
✓ Branch 6 → 7 taken 3 times.
3 const auto parsed = ComposeRecipients::parse(trimmed);
222
3/4
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 13 taken 1 time.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 13 not taken.
3 return parsed && parsed->headers.size() == 1;
223 3 }
224
225 5 void RecipientChipModel::addCompletion(const QVariantMap &row) {
226
4/4
✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 72 taken 3 times.
10 if (row.value(QStringLiteral("group")).toBool()) {
227 2 Chip chip;
228 2 chip.state = QStringLiteral("group");
229
2/2
✓ Branch 23 → 24 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
4 chip.name = row.value(QStringLiteral("name")).toString();
230
3/3
✓ Branch 35 → 36 taken 2 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 37 → 38 taken 2 times.
4 const auto parsed = ComposeRecipients::parse(row.value(QStringLiteral("insert")).toString());
231
1/2
✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 58 not taken.
2 if (parsed) {
232
2/2
✓ Branch 56 → 48 taken 6 times.
✓ Branch 56 → 57 taken 2 times.
8 for (const QString &member : parsed->headers)
233
5/5
✓ Branch 49 → 50 taken 6 times.
✓ Branch 50 → 51 taken 6 times.
✓ Branch 52 → 53 taken 5 times.
✓ Branch 52 → 54 taken 1 time.
✓ Branch 53 → 54 taken 5 times.
6 if (!containsAddress(splitMailbox(member).second)) chip.members.append(member);
234 }
235
1/2
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 2 times.
2 if (chip.members.isEmpty()) return;
236
1/1
✓ Branch 61 → 62 taken 2 times.
2 append(chip);
237
2/4
✓ Branch 64 → 65 taken 2 times.
✗ Branch 64 → 66 not taken.
✓ Branch 68 → 69 taken 2 times.
✗ Branch 68 → 71 not taken.
2 } else {
238
5/5
✓ Branch 76 → 77 taken 3 times.
✓ Branch 77 → 78 taken 3 times.
✓ Branch 82 → 83 taken 3 times.
✓ Branch 83 → 84 taken 3 times.
✓ Branch 84 → 85 taken 3 times.
9 addMailbox(row.value(QStringLiteral("name")).toString(), row.value(QStringLiteral("email")).toString());
239 3 return;
240 }
241 2 emit changed();
242 }
243
244 8 void RecipientChipModel::addMailbox(const QString &name, const QString &address) {
245
5/5
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 7 times.
✓ Branch 5 → 6 taken 7 times.
✓ Branch 6 → 7 taken 8 times.
✓ Branch 7 → 8 taken 8 times.
8 append(chipFor(name.isEmpty() ? address : ComposeRecipients::mailbox(name, address)));
246 8 emit changed();
247 8 }
248
249 4 void RecipientChipModel::remove(int row) {
250
3/6
✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 4 times.
4 if (row < 0 || row >= m_chips.size()) return;
251
1/1
✓ Branch 10 → 11 taken 4 times.
4 beginRemoveRows({}, row, row);
252 4 m_chips.removeAt(row);
253 4 endRemoveRows();
254 4 emit changed();
255 }
256
257 1 QString RecipientChipModel::take(int row) {
258
3/6
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (row < 0 || row >= m_chips.size()) return {};
259 1 const Chip chip = m_chips.at(row);
260
1/1
✓ Branch 11 → 12 taken 1 time.
1 remove(row);
261
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1 time.
1 if (chip.state == QLatin1String("invalid")) return chip.raw;
262
1/3
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 27 taken 1 time.
✗ Branch 22 → 23 not taken.
1 if (chip.state == QLatin1String("group")) return chip.members.join(QStringLiteral(", "));
263
2/3
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
1 return chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address);
264 1 }
265
266 2 void RecipientChipModel::expandGroup(int row) {
267
4/8
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 9 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 2 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 2 times.
2 if (row < 0 || row >= m_chips.size() || m_chips.at(row).state != QLatin1String("group")) return;
268 2 const QStringList members = m_chips.at(row).members;
269
1/1
✓ Branch 16 → 17 taken 2 times.
2 beginRemoveRows({}, row, row);
270
1/1
✓ Branch 17 → 18 taken 2 times.
2 m_chips.removeAt(row);
271
1/1
✓ Branch 18 → 19 taken 2 times.
2 endRemoveRows();
272 2 QList<Chip> chips;
273
2/2
✓ Branch 35 → 21 taken 5 times.
✓ Branch 35 → 36 taken 2 times.
7 for (const QString &member : members) {
274
1/1
✓ Branch 22 → 23 taken 5 times.
5 const Chip chip = chipFor(member);
275
5/8
✓ Branch 25 → 26 taken 5 times.
✗ Branch 25 → 29 not taken.
✓ Branch 26 → 27 taken 5 times.
✓ Branch 27 → 28 taken 5 times.
✗ Branch 27 → 29 not taken.
✓ Branch 30 → 31 taken 5 times.
✗ Branch 30 → 32 not taken.
✓ Branch 31 → 32 taken 5 times.
5 if (chip.state != QLatin1String("invalid") && !containsAddress(chip.address)) chips.append(chip);
276 5 }
277
1/2
✓ Branch 37 → 38 taken 2 times.
✗ Branch 37 → 48 not taken.
2 if (!chips.isEmpty()) {
278
1/1
✓ Branch 40 → 41 taken 2 times.
2 beginInsertRows({}, row, row + int(chips.size()) - 1);
279
3/3
✓ Branch 43 → 44 taken 5 times.
✓ Branch 46 → 42 taken 5 times.
✓ Branch 46 → 47 taken 2 times.
7 for (qsizetype i = 0; i < chips.size(); ++i) m_chips.insert(row + i, chips.at(i));
280
1/1
✓ Branch 47 → 48 taken 2 times.
2 endInsertRows();
281 }
282
1/1
✓ Branch 48 → 49 taken 2 times.
2 emit changed();
283 2 }
284
285 761 QVariantMap RecipientChipModel::chipAt(int row) const {
286
6/6
✓ Branch 2 → 3 taken 734 times.
✓ Branch 2 → 5 taken 27 times.
✓ Branch 4 → 5 taken 31 times.
✓ Branch 4 → 6 taken 703 times.
✓ Branch 7 → 8 taken 58 times.
✓ Branch 7 → 9 taken 703 times.
761 if (row < 0 || row >= m_chips.size()) return {};
287 703 const Chip &chip = m_chips.at(row);
288 2109 return {{QStringLiteral("name"), chip.name}, {QStringLiteral("address"), chip.address},
289 2109 {QStringLiteral("state"), chip.state}, {QStringLiteral("members"), chip.members},
290
2/4
✓ Branch 32 → 33 taken 3515 times.
✓ Branch 32 → 34 taken 703 times.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 49 not taken.
5624 {QStringLiteral("raw"), chip.raw}};
291 4218 }
292
293 2 void RecipientChipModel::insertChip(int row, const QVariantMap &value) {
294 2 Chip chip;
295
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
4 chip.name = value.value(QStringLiteral("name")).toString();
296
2/2
✓ Branch 18 → 19 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
4 chip.address = value.value(QStringLiteral("address")).toString();
297
2/2
✓ Branch 30 → 31 taken 2 times.
✓ Branch 31 → 32 taken 2 times.
4 chip.state = value.value(QStringLiteral("state")).toString();
298
2/2
✓ Branch 42 → 43 taken 2 times.
✓ Branch 43 → 44 taken 2 times.
4 chip.members = value.value(QStringLiteral("members")).toStringList();
299
2/2
✓ Branch 54 → 55 taken 2 times.
✓ Branch 55 → 56 taken 2 times.
4 chip.raw = value.value(QStringLiteral("raw")).toString();
300
3/6
✓ Branch 64 → 65 taken 2 times.
✗ Branch 64 → 69 not taken.
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 69 not taken.
✓ Branch 70 → 71 taken 2 times.
✗ Branch 70 → 80 not taken.
2 if (chip.state != QLatin1String("group") && chip.state != QLatin1String("invalid"))
301
4/4
✓ Branch 72 → 73 taken 1 time.
✓ Branch 72 → 74 taken 1 time.
✓ Branch 74 → 75 taken 1 time.
✓ Branch 75 → 76 taken 2 times.
2 chip = chipFor(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address));
302
5/9
✓ Branch 82 → 83 taken 2 times.
✗ Branch 82 → 89 not taken.
✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 89 not taken.
✓ Branch 86 → 87 taken 2 times.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 2 times.
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 93 taken 2 times.
2 if (chip.state != QLatin1String("invalid") && chip.state != QLatin1String("group") && containsAddress(chip.address)) {
303 ✗ emit duplicateSkipped(chip.address);
304 ✗ return;
305 }
306
1/1
✓ Branch 94 → 95 taken 2 times.
2 row = qBound(0, row, int(m_chips.size()));
307
1/1
✓ Branch 96 → 97 taken 2 times.
2 beginInsertRows({}, row, row);
308
1/1
✓ Branch 97 → 98 taken 2 times.
2 m_chips.insert(row, chip);
309
1/1
✓ Branch 98 → 99 taken 2 times.
2 endInsertRows();
310
1/1
✓ Branch 99 → 100 taken 2 times.
2 emit changed();
311 2 }
312
313 352 QString RecipientChipModel::copyText(const QList<int> &rows) const {
314 352 QStringList out;
315
2/2
✓ Branch 30 → 4 taken 352 times.
✓ Branch 30 → 31 taken 352 times.
704 for (const int row : rows) {
316
6/6
✓ Branch 5 → 6 taken 325 times.
✓ Branch 5 → 8 taken 27 times.
✓ Branch 7 → 8 taken 31 times.
✓ Branch 7 → 9 taken 294 times.
✓ Branch 10 → 11 taken 58 times.
✓ Branch 10 → 12 taken 294 times.
352 if (row < 0 || row >= m_chips.size()) continue;
317 294 const Chip &chip = m_chips.at(row);
318
3/3
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 293 times.
✓ Branch 16 → 28 taken 1 time.
294 if (chip.state == QLatin1String("group")) out.append(chip.members);
319
3/3
✓ Branch 19 → 20 taken 27 times.
✓ Branch 19 → 21 taken 266 times.
✓ Branch 20 → 28 taken 27 times.
293 else if (chip.state == QLatin1String("invalid")) out.append(chip.raw);
320
4/4
✓ Branch 22 → 23 taken 144 times.
✓ Branch 22 → 24 taken 122 times.
✓ Branch 24 → 25 taken 122 times.
✓ Branch 25 → 26 taken 266 times.
266 else out.append(chip.name.isEmpty() ? chip.address : ComposeRecipients::mailbox(chip.name, chip.address));
321 }
322
1/1
✓ Branch 34 → 35 taken 352 times.
704 return out.join(QStringLiteral(", "));
323 352 }
324
325 ✗ void RecipientChipModel::clear() {
326 ✗ beginResetModel();
327 ✗ m_chips.clear();
328 ✗ endResetModel();
329 ✗ emit changed();
330 ✗ }
331