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: 92.7% 357 / 0 / 385
Functions: 100.0% 34 / 0 / 34
Branches: 77.8% 385 / 0 / 495

presentation/QuickContactModel.cpp
Line Branch Exec Source
1 #include "QuickContactModel.h"
2
3 #include "ContactEditorFields.h"
4 #include "ContactPeopleView.h"
5 #include "data/ContactIndex.h"
6 #include "data/ContactStore.h"
7 #include "data/VCardCodec.h"
8 #include "util/MailboxText.h"
9
10 #include <QMetaObject>
11 #include <QSet>
12 #include <QSettings>
13 #include <algorithm>
14
15 namespace {
16
17 1866 QString initialsOf(const QString &displayName) {
18
1/1
✓ Branch 5 → 6 taken 1866 times.
1866 const QStringList words = displayName.split(QLatin1Char(' '), Qt::SkipEmptyParts);
19 1866 QString result;
20
5/9
✓ Branch 6 → 7 taken 1866 times.
✓ Branch 8 → 9 taken 1857 times.
✓ Branch 8 → 11 taken 9 times.
✓ Branch 24 → 14 taken 3732 times.
✓ Branch 24 → 25 taken 1866 times.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 44 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
9330 for (const QString &word : {words.value(0), words.size() > 1 ? words.last() : QString()})
21
2/2
✓ Branch 22 → 16 taken 3723 times.
✓ Branch 22 → 23 taken 9 times.
3732 for (const QChar c : word)
22
1/2
✓ Branch 17 → 18 taken 3723 times.
✗ Branch 17 → 21 not taken.
3723 if (c.isLetterOrNumber()) {
23
1/1
✓ Branch 19 → 20 taken 3723 times.
3723 result += c.toUpper();
24 3723 break;
25 5598 }
26
1/1
✓ Branch 31 → 32 taken 1866 times.
3732 return result.left(2);
27 1866 }
28
29 863 ContactQueries::View viewFromMap(const QVariantMap &map, bool byFamily) {
30 863 ContactQueries::View view;
31
2/2
✓ Branch 6 → 7 taken 863 times.
✓ Branch 7 → 8 taken 863 times.
1726 const QString kind = map.value(QStringLiteral("kind")).toString();
32
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 863 times.
863 if (kind == QLatin1String("frequent"))
33 ✗ view.kind = ContactQueries::ViewKind::Frequent;
34
2/2
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 20 taken 860 times.
863 else if (kind == QLatin1String("collected"))
35 3 view.kind = ContactQueries::ViewKind::Collected;
36
2/2
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 24 taken 859 times.
860 else if (kind == QLatin1String("book"))
37 1 view.kind = ContactQueries::ViewKind::Book;
38
2/2
✓ Branch 26 → 27 taken 2 times.
✓ Branch 26 → 28 taken 857 times.
859 else if (kind == QLatin1String("group"))
39 2 view.kind = ContactQueries::ViewKind::Group;
40
2/2
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 32 taken 856 times.
857 else if (kind == QLatin1String("duplicates"))
41 1 view.kind = ContactQueries::ViewKind::Duplicates;
42
2/2
✓ Branch 34 → 35 taken 1 time.
✓ Branch 34 → 36 taken 855 times.
856 else if (kind == QLatin1String("ungrouped"))
43 1 view.kind = ContactQueries::ViewKind::Ungrouped;
44
2/2
✓ Branch 40 → 41 taken 863 times.
✓ Branch 41 → 42 taken 863 times.
1726 view.bookId = map.value(QStringLiteral("bookId")).toLongLong();
45
2/2
✓ Branch 50 → 51 taken 863 times.
✓ Branch 51 → 52 taken 863 times.
1726 view.group = map.value(QStringLiteral("group")).toString();
46 863 view.byFamilyName = byFamily;
47 863 return view;
48 863 }
49
50 // S106-310: the in-memory order is the SQL order (ContactQueries::sortKeyLess).
51 101 bool before(const ContactQueries::Row &a, const ContactQueries::Row &b) {
52
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 5 taken 97 times.
101 return a.sortKey == b.sortKey ? a.cardId < b.cardId : ContactQueries::sortKeyLess(a.sortKey, b.sortKey);
53 }
54
55 } // namespace
56
57 854 QuickContactModel::QuickContactModel(QString databasePath, QObject *parent)
58
2/2
✓ Branch 10 → 11 taken 854 times.
✓ Branch 11 → 12 taken 854 times.
854 : QAbstractListModel(parent), m_databasePath(std::move(databasePath)) {
59
1/1
✓ Branch 14 → 15 taken 854 times.
854 m_pool.setMaxThreadCount(1);
60
1/1
✓ Branch 15 → 16 taken 854 times.
854 m_completionPool.setMaxThreadCount(1);
61 // S97.3.7: the last view and sort order survive a restart.
62
1/1
✓ Branch 16 → 17 taken 854 times.
854 const QSettings settings;
63
3/3
✓ Branch 28 → 29 taken 854 times.
✓ Branch 29 → 30 taken 854 times.
✓ Branch 30 → 31 taken 854 times.
1708 m_view = viewFromMap(settings.value(QStringLiteral("contacts/view")).toMap(),
64
2/2
✓ Branch 22 → 23 taken 854 times.
✓ Branch 23 → 24 taken 854 times.
3416 settings.value(QStringLiteral("contacts/sortByFamilyName"), false).toBool());
65 // F100-10/F100-13: the chosen columns and the pane width.
66
3/3
✓ Branch 45 → 46 taken 854 times.
✓ Branch 48 → 49 taken 1 time.
✓ Branch 48 → 61 taken 853 times.
854 if (settings.contains(QStringLiteral("contacts/columns")))
67
2/2
✓ Branch 53 → 54 taken 1 time.
✓ Branch 54 → 55 taken 1 time.
1 m_columns = settings.value(QStringLiteral("contacts/columns")).toStringList();
68 else
69
2/4
✓ Branch 72 → 73 taken 2559 times.
✓ Branch 72 → 74 taken 853 times.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
3412 m_columns = {QStringLiteral("email"), QStringLiteral("organization"), QStringLiteral("book")};
70
2/2
✓ Branch 2 → 3 taken 2561 times.
✓ Branch 78 → 79 taken 854 times.
3415 m_columns.removeIf([](const QString &id) { return !availableColumns().contains(id); });
71
1/1
✓ Branch 79 → 80 taken 854 times.
854 m_columns.removeDuplicates();
72
3/3
✓ Branch 85 → 86 taken 854 times.
✓ Branch 86 → 87 taken 854 times.
✓ Branch 87 → 88 taken 854 times.
1708 m_paneWidth = qBound(320, settings.value(QStringLiteral("contacts/paneWidth"), 440).toInt(), 760);
73
1/1
✓ Branch 92 → 93 taken 854 times.
854 loadPage();
74 4266 }
75
76 889 QuickContactModel::~QuickContactModel() {
77 // F4: an explicit join makes the worker lifetime deterministic.
78 854 m_pool.waitForDone();
79 854 m_completionPool.waitForDone();
80 889 }
81
82 170 QHash<int, QByteArray> QuickContactModel::roleNames() const {
83 ✗ return {{CardKey, "cardKey"}, {DisplayName, "displayName"}, {PrimaryAddress, "primaryAddress"},
84 ✗ {AddressCount, "addressCount"}, {Organization, "organization"}, {BookName, "bookName"},
85 ✗ {BookReadOnly, "bookReadOnly"}, {LocalBook, "localBook"}, {Section, "section"},
86 ✗ {Initials, "initials"}, {AvatarColor, "avatarColor"}, {HasPhoto, "hasPhoto"},
87 ✗ {IsGroup, "isGroup"}, {SyncState, "syncState"}, {Collected, "collected"},
88 ✗ {Writable, "writable"}, {BookId, "bookId"}, {Uid, "uid"},
89 ✗ {PhotoTag, "photoTag"}, {Phone, "phone"}, {JobTitle, "jobTitle"},
90
2/4
✓ Branch 28 → 29 taken 4080 times.
✓ Branch 28 → 30 taken 170 times.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
4250 {Locality, "locality"}, {Birthday, "birthday"}, {Groups, "groups"}};
91 170 }
92
93
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 69652 times.
69652 int QuickContactModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : int(m_rows.size()); }
94
95 44183 QVariant QuickContactModel::rowData(const ContactQueries::Row &row, int role) const {
96
20/25
✓ Branch 2 → 3 taken 1894 times.
✓ Branch 2 → 4 taken 1878 times.
✓ Branch 2 → 5 taken 1855 times.
✓ Branch 2 → 6 taken 5 times.
✓ Branch 2 → 7 taken 1854 times.
✓ Branch 2 → 8 taken 1857 times.
✗ Branch 2 → 9 not taken.
✓ Branch 2 → 10 taken 1852 times.
✓ Branch 2 → 11 taken 10759 times.
✓ Branch 2 → 12 taken 1853 times.
✓ Branch 2 → 16 taken 1852 times.
✓ Branch 2 → 21 taken 1852 times.
✓ Branch 2 → 22 taken 1852 times.
✓ Branch 2 → 23 taken 1852 times.
✓ Branch 2 → 27 taken 1852 times.
✓ Branch 2 → 28 taken 1853 times.
✗ Branch 2 → 29 not taken.
✗ Branch 2 → 30 not taken.
✗ Branch 2 → 31 not taken.
✓ Branch 2 → 32 taken 1853 times.
✓ Branch 2 → 33 taken 1853 times.
✓ Branch 2 → 34 taken 1853 times.
✓ Branch 2 → 35 taken 1852 times.
✓ Branch 2 → 36 taken 1852 times.
✗ Branch 2 → 37 not taken.
44183 switch (role) {
97 1894 case CardKey: return row.key;
98 1878 case DisplayName: return row.displayName;
99 1855 case PrimaryAddress: return row.primaryAddress;
100 5 case AddressCount: return row.addressCount;
101 1854 case Organization: return row.organization;
102 1857 case BookName: return row.bookName;
103 ✗ case BookReadOnly: return row.bookReadOnly;
104 1852 case LocalBook: return row.localBook;
105 10759 case Section: return row.section;
106
1/1
✓ Branch 12 → 13 taken 1853 times.
1853 case Initials: return initialsOf(row.displayName);
107
1/2
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1852 times.
1852 case AvatarColor: return ContactIndex::avatarColorIndex(row.collected ? row.primaryAddress : row.uid);
108 1852 case HasPhoto: return row.hasPhoto;
109 1852 case PhotoTag: return row.photoTag;
110 1852 case IsGroup: return row.kind == QLatin1String("group");
111 1852 case SyncState: return row.syncState;
112 1853 case Collected: return row.collected;
113 ✗ case Writable: return row.writable;
114 ✗ case BookId: return row.bookId;
115 ✗ case Uid: return row.uid;
116 1853 case Phone: return row.phone;
117 1853 case JobTitle: return row.title;
118 1853 case Locality: return row.locality;
119 1852 case Birthday: return row.birthday;
120 1852 case Groups: return row.groups;
121 ✗ default: return {};
122 }
123 }
124
125 44183 QVariant QuickContactModel::data(const QModelIndex &index, int role) const {
126
3/6
✓ Branch 3 → 4 taken 44183 times.
✗ Branch 3 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 44183 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 44183 times.
44183 if (index.row() < 0 || index.row() >= m_rows.size())
127 ✗ return {};
128 44183 return rowData(m_rows.at(index.row()), role);
129 }
130
131 217 bool QuickContactModel::canFetchMore(const QModelIndex &parent) const {
132
5/6
✓ Branch 3 → 4 taken 217 times.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 30 times.
✓ Branch 4 → 7 taken 187 times.
✓ Branch 5 → 6 taken 28 times.
✓ Branch 5 → 7 taken 2 times.
217 return !parent.isValid() && m_hasMore && !m_loading;
133 }
134
135 18 void QuickContactModel::fetchMore(const QModelIndex &parent) {
136
2/2
✓ Branch 3 → 4 taken 13 times.
✓ Branch 3 → 5 taken 5 times.
18 if (canFetchMore(parent))
137 13 loadPage();
138 18 }
139
140 55 void QuickContactModel::setFilter(const QString &filter) {
141
2/2
✓ Branch 3 → 4 taken 14 times.
✓ Branch 3 → 5 taken 41 times.
55 if (m_filter == filter)
142 14 return;
143 41 ++m_generation; // invalidates a page in flight
144 41 m_filter = filter;
145 41 emit filterChanged();
146 41 restartProjection();
147 }
148
149 1040 QVariantMap QuickContactModel::view() const {
150 static const char *kinds[] = {"all", "frequent", "collected", "book", "group", "duplicates", "ungrouped"};
151
1/1
✓ Branch 6 → 7 taken 1040 times.
2080 return {{QStringLiteral("kind"), QString::fromLatin1(kinds[int(m_view.kind)])},
152 2080 {QStringLiteral("bookId"), m_view.bookId},
153
2/4
✓ Branch 18 → 19 taken 3120 times.
✓ Branch 18 → 20 taken 1040 times.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 32 not taken.
6240 {QStringLiteral("group"), m_view.group}};
154 4160 }
155
156 9 void QuickContactModel::setView(const QVariantMap &map) {
157
1/1
✓ Branch 2 → 3 taken 9 times.
9 const ContactQueries::View next = viewFromMap(map, m_view.byFamilyName);
158
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 9 times.
9 if (next == m_view)
159 ✗ return;
160 9 ++m_generation;
161 9 m_view = next;
162
3/3
✓ Branch 7 → 8 taken 9 times.
✓ Branch 8 → 9 taken 9 times.
✓ Branch 14 → 15 taken 9 times.
18 QSettings().setValue(QStringLiteral("contacts/view"), view());
163
1/1
✓ Branch 20 → 21 taken 9 times.
9 emit viewChanged();
164
1/1
✓ Branch 21 → 22 taken 9 times.
9 restartProjection();
165 9 }
166
167 2 void QuickContactModel::setSortByFamilyName(bool byFamily) {
168
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2 times.
2 if (m_view.byFamilyName == byFamily)
169 ✗ return;
170 2 ++m_generation;
171 2 m_view.byFamilyName = byFamily;
172
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
4 QSettings().setValue(QStringLiteral("contacts/sortByFamilyName"), byFamily);
173 2 emit viewChanged();
174 2 restartProjection();
175 }
176
177 7 void QuickContactModel::reload() { restartProjection(); }
178
179 2662 QStringList QuickContactModel::availableColumns() {
180 2662 return {QStringLiteral("email"), QStringLiteral("phone"), QStringLiteral("organization"), QStringLiteral("title"),
181
2/4
✓ Branch 28 → 29 taken 21296 times.
✓ Branch 28 → 30 taken 2662 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
26620 QStringLiteral("city"), QStringLiteral("birthday"), QStringLiteral("groups"), QStringLiteral("book")};
182 23958 }
183
184 6 void QuickContactModel::setColumns(const QStringList &columns) {
185 6 QStringList valid;
186
2/2
✓ Branch 18 → 4 taken 22 times.
✓ Branch 18 → 19 taken 6 times.
28 for (const QString &id : columns)
187
9/12
✓ Branch 5 → 6 taken 22 times.
✓ Branch 7 → 8 taken 21 times.
✓ Branch 7 → 11 taken 1 time.
✓ Branch 9 → 10 taken 20 times.
✓ Branch 9 → 11 taken 1 time.
✓ Branch 12 → 13 taken 22 times.
✗ Branch 12 → 14 not taken.
✓ Branch 14 → 15 taken 20 times.
✓ Branch 14 → 16 taken 2 times.
✓ Branch 15 → 16 taken 20 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
22 if (availableColumns().contains(id) && !valid.contains(id)) valid.append(id);
188
2/3
✓ Branch 19 → 20 taken 6 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 6 times.
6 if (valid == m_columns)
189 ✗ return;
190 6 m_columns = valid;
191
2/2
✓ Branch 23 → 24 taken 6 times.
✓ Branch 29 → 30 taken 6 times.
12 QSettings().setValue(QStringLiteral("contacts/columns"), m_columns);
192
1/1
✓ Branch 34 → 35 taken 6 times.
6 emit columnsChanged();
193 6 }
194
195 7 void QuickContactModel::setPaneWidth(int width) {
196
1/1
✓ Branch 2 → 3 taken 7 times.
7 width = qBound(320, width, 760);
197
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 7 times.
7 if (width == m_paneWidth)
198 ✗ return;
199 7 m_paneWidth = width;
200
2/2
✓ Branch 5 → 6 taken 7 times.
✓ Branch 11 → 12 taken 7 times.
14 QSettings().setValue(QStringLiteral("contacts/paneWidth"), m_paneWidth);
201 7 emit paneWidthChanged();
202 }
203
204 422 int QuickContactModel::indexOfKey(const QString &key) const {
205
2/2
✓ Branch 9 → 3 taken 10260 times.
✓ Branch 9 → 10 taken 113 times.
10373 for (int i = 0; i < m_rows.size(); ++i)
206
2/2
✓ Branch 5 → 6 taken 309 times.
✓ Branch 5 → 7 taken 9951 times.
10260 if (m_rows.at(i).key == key)
207 309 return i;
208 113 return -1;
209 }
210
211 153 QString QuickContactModel::keyAt(int row) const {
212
4/4
✓ Branch 2 → 3 taken 146 times.
✓ Branch 2 → 7 taken 7 times.
✓ Branch 4 → 5 taken 142 times.
✓ Branch 4 → 7 taken 4 times.
153 return row >= 0 && row < m_rows.size() ? m_rows.at(row).key : QString();
213 }
214
215 857 bool QuickContactModel::ensureReader() const {
216
5/6
✓ Branch 3 → 4 taken 774 times.
✓ Branch 3 → 8 taken 83 times.
✓ Branch 6 → 7 taken 774 times.
✗ Branch 6 → 8 not taken.
✓ Branch 9 → 10 taken 774 times.
✓ Branch 9 → 11 taken 83 times.
857 if (m_reader && m_reader->isOpen())
217 774 return true;
218
1/1
✓ Branch 11 → 12 taken 83 times.
83 m_reader = std::make_unique<ContactStore>();
219
3/6
✓ Branch 15 → 16 taken 83 times.
✗ Branch 15 → 19 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 83 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 83 times.
83 if (m_databasePath.isEmpty() || !m_reader->open(m_databasePath)) {
220 ✗ m_reader.reset();
221 ✗ return false;
222 }
223 83 return true;
224 }
225
226 773 QVariantMap QuickContactModel::card(const QString &key) const {
227
2/3
✓ Branch 2 → 3 taken 773 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 773 times.
773 if (!ensureReader())
228 ✗ return {};
229
3/3
✓ Branch 6 → 7 taken 773 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 88 taken 772 times.
773 if (key.startsWith(QLatin1String("collected:"))) {
230
1/1
✓ Branch 8 → 9 taken 1 time.
1 const QString address = key.mid(10);
231
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
1 const ContactQueries::Lookup found = ContactQueries::lookup(m_reader->database(), address);
232
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1 time.
1 const QString name = found.displayName.isEmpty() ? address : found.displayName;
233 2 return {{QStringLiteral("key"), key},
234 2 {QStringLiteral("collected"), true},
235 2 {QStringLiteral("displayName"), name},
236
1/1
✓ Branch 33 → 34 taken 1 time.
2 {QStringLiteral("initials"), initialsOf(name)},
237
1/1
✓ Branch 38 → 39 taken 1 time.
2 {QStringLiteral("avatarColor"), ContactIndex::avatarColorIndex(address)},
238
4/8
✓ Branch 63 → 64 taken 1 time.
✓ Branch 63 → 65 taken 1 time.
✓ Branch 67 → 68 taken 1 time.
✓ Branch 67 → 69 taken 1 time.
✗ Branch 359 → 360 not taken.
✗ Branch 359 → 361 not taken.
✗ Branch 365 → 366 not taken.
✗ Branch 365 → 367 not taken.
5 {QStringLiteral("emails"), QVariantList{QVariantMap{{QStringLiteral("value"), address}}}},
239
2/4
✓ Branch 57 → 58 taken 7 times.
✓ Branch 57 → 59 taken 1 time.
✗ Branch 346 → 347 not taken.
✗ Branch 346 → 348 not taken.
10 {QStringLiteral("writable"), false}};
240 1 }
241 772 qint64 bookId = 0;
242 772 QString uid;
243
3/3
✓ Branch 88 → 89 taken 772 times.
✓ Branch 89 → 90 taken 1 time.
✓ Branch 89 → 91 taken 771 times.
772 if (!ContactQueries::splitKey(key, &bookId, &uid))
244 1 return {};
245
1/1
✓ Branch 92 → 93 taken 771 times.
771 const auto stored = m_reader->card(bookId, uid);
246
1/1
✓ Branch 94 → 95 taken 771 times.
771 const auto book = m_reader->book(bookId);
247
5/6
✓ Branch 96 → 97 taken 750 times.
✓ Branch 96 → 99 taken 21 times.
✗ Branch 98 → 99 not taken.
✓ Branch 98 → 100 taken 750 times.
✓ Branch 101 → 102 taken 21 times.
✓ Branch 101 → 103 taken 750 times.
771 if (!stored || !book)
248 21 return {};
249
1/1
✓ Branch 103 → 104 taken 750 times.
750 ContactCard parsed;
250 750 bool writable = true;
251
2/3
✓ Branch 105 → 106 taken 750 times.
✗ Branch 106 → 107 not taken.
✓ Branch 106 → 108 taken 750 times.
750 if (!VCardCodec::parse(stored->raw, &parsed, nullptr, &writable))
252 ✗ writable = false;
253
1/1
✓ Branch 108 → 109 taken 750 times.
750 QVariantMap map = ContactEditorFields::toMap(parsed);
254
3/3
✓ Branch 109 → 110 taken 750 times.
✓ Branch 110 → 111 taken 750 times.
✓ Branch 115 → 116 taken 750 times.
1500 map.insert(QStringLiteral("birthdayText"), ContactEditorFields::formatPartialDate(parsed.birthday));
255
3/3
✓ Branch 121 → 122 taken 750 times.
✓ Branch 122 → 123 taken 750 times.
✓ Branch 127 → 128 taken 750 times.
1500 map.insert(QStringLiteral("anniversaryText"), ContactEditorFields::formatPartialDate(parsed.anniversary));
256
1/1
✓ Branch 133 → 134 taken 750 times.
750 const QString name = ContactIndex::displayName(parsed);
257
1/1
✓ Branch 138 → 139 taken 750 times.
1500 map.insert(QStringLiteral("key"), key);
258
1/1
✓ Branch 146 → 147 taken 750 times.
1500 map.insert(QStringLiteral("bookId"), bookId);
259
1/1
✓ Branch 155 → 156 taken 750 times.
1500 map.insert(QStringLiteral("bookName"), book->displayName);
260
1/1
✓ Branch 164 → 165 taken 750 times.
1500 map.insert(QStringLiteral("accountId"), book->accountId);
261
1/1
✓ Branch 174 → 175 taken 750 times.
1500 map.insert(QStringLiteral("localBook"), book->isLocal());
262
1/1
✓ Branch 183 → 184 taken 750 times.
1500 map.insert(QStringLiteral("bookReadOnly"), book->readOnly);
263
1/1
✓ Branch 191 → 192 taken 750 times.
1500 map.insert(QStringLiteral("displayName"), name);
264
2/2
✓ Branch 195 → 196 taken 750 times.
✓ Branch 200 → 201 taken 750 times.
1500 map.insert(QStringLiteral("initials"), ContactIndex::initials(parsed));
265
2/2
✓ Branch 205 → 206 taken 750 times.
✓ Branch 210 → 211 taken 750 times.
1500 map.insert(QStringLiteral("avatarColor"), ContactIndex::avatarColorIndex(uid));
266
1/1
✓ Branch 219 → 220 taken 750 times.
1500 map.insert(QStringLiteral("syncState"), stored->syncState);
267
3/3
✓ Branch 232 → 233 taken 750 times.
✓ Branch 233 → 234 taken 750 times.
✓ Branch 238 → 239 taken 750 times.
1500 map.insert(QStringLiteral("people"), ContactPeopleView::build(m_reader->database(), stored->id, parsed, name,
268
2/2
✓ Branch 228 → 229 taken 750 times.
✓ Branch 229 → 230 taken 750 times.
1500 QSettings().value(QStringLiteral("contacts/meCard")).toString(),
269
1/1
✓ Branch 223 → 224 taken 750 times.
750 m_eventTitle));
270 // The avatar URL changes with the photo (inline data or a fetched URI).
271
1/1
✓ Branch 262 → 263 taken 750 times.
1500 map.insert(QStringLiteral("photoTag"),
272
3/3
✓ Branch 249 → 250 taken 110 times.
✓ Branch 249 → 251 taken 640 times.
✓ Branch 250 → 258 taken 110 times.
2140 parsed.photo.kind == ContactPhoto::Kind::Inline ? QString::number(qHash(parsed.photo.data), 16)
273
4/7
✓ Branch 253 → 254 taken 1 time.
✓ Branch 255 → 256 taken 1 time.
✗ Branch 255 → 257 not taken.
✓ Branch 267 → 268 taken 1 time.
✓ Branch 267 → 269 taken 749 times.
✗ Branch 606 → 607 not taken.
✗ Branch 606 → 608 not taken.
751 : parsed.photo.kind == ContactPhoto::Kind::Uri && !m_reader->fetchedPhoto(bookId, uid).isEmpty()
274
2/2
✓ Branch 251 → 252 taken 1 time.
✓ Branch 251 → 257 taken 639 times.
640 ? parsed.photo.uri
275 : QString());
276
1/1
✓ Branch 284 → 285 taken 750 times.
1500 map.insert(QStringLiteral("writable"),
277
4/8
✓ Branch 269 → 270 taken 750 times.
✗ Branch 269 → 279 not taken.
✓ Branch 271 → 272 taken 750 times.
✗ Branch 271 → 279 not taken.
✓ Branch 273 → 274 taken 750 times.
✗ Branch 273 → 279 not taken.
✓ Branch 277 → 278 taken 750 times.
✗ Branch 277 → 279 not taken.
750 writable && stored->writable && !book->readOnly && stored->syncState != QLatin1String("legacy"));
278
3/3
✓ Branch 289 → 290 taken 750 times.
✓ Branch 291 → 292 taken 126 times.
✓ Branch 291 → 331 taken 624 times.
750 if (const auto entry = m_reader->outboxEntry(bookId, uid)) {
279
1/1
✓ Branch 299 → 300 taken 126 times.
252 map.insert(QStringLiteral("conflict"), entry->state == QLatin1String("conflict"));
280
1/1
✓ Branch 308 → 309 taken 126 times.
252 map.insert(QStringLiteral("conflictFields"), entry->conflictFields);
281
1/1
✓ Branch 317 → 318 taken 126 times.
252 map.insert(QStringLiteral("pendingAttempts"), entry->attempts);
282 // Technical detail only behind "Details" (G16).
283
1/1
✓ Branch 326 → 327 taken 126 times.
252 map.insert(QStringLiteral("lastError"), entry->lastError);
284 750 }
285 750 return map;
286 783 }
287
288 19 int QuickContactModel::insertPosition(const ContactQueries::Row &row) const {
289
1/1
✓ Branch 4 → 5 taken 19 times.
19 const auto it = std::lower_bound(m_rows.cbegin(), m_rows.cend(), row, before);
290 38 return int(it - m_rows.cbegin());
291 }
292
293 42 void QuickContactModel::refreshCount() {
294
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 42 times.
42 if (!ensureReader())
295 ✗ return;
296
2/2
✓ Branch 6 → 7 taken 42 times.
✓ Branch 7 → 8 taken 42 times.
42 const int count = ContactQueries::count(m_reader->database(), m_view, m_filter);
297
2/2
✓ Branch 9 → 10 taken 16 times.
✓ Branch 9 → 11 taken 26 times.
42 if (count != m_totalCount) {
298 16 m_totalCount = count;
299 16 emit totalCountChanged();
300 }
301 }
302
303 47 void QuickContactModel::applyCardChanges(const QStringList &keys) {
304
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 47 times.
47 if (keys.isEmpty())
305 ✗ return;
306 // Smart lists over usage and collected addresses are small: reload them.
307 // A changed card can make an unchanged one a duplicate: reload that too.
308 141 const bool cardView = m_view.kind != ContactQueries::ViewKind::Collected &&
309
2/4
✓ Branch 5 → 6 taken 47 times.
✗ Branch 5 → 9 not taken.
✓ Branch 6 → 7 taken 47 times.
✗ Branch 6 → 9 not taken.
94 m_view.kind != ContactQueries::ViewKind::Frequent &&
310
1/2
✓ Branch 7 → 8 taken 47 times.
✗ Branch 7 → 9 not taken.
47 m_view.kind != ContactQueries::ViewKind::Duplicates;
311
7/9
✓ Branch 10 → 11 taken 47 times.
✗ Branch 10 → 17 not taken.
✓ Branch 14 → 15 taken 42 times.
✓ Branch 14 → 17 taken 5 times.
✓ Branch 15 → 16 taken 42 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 42 times.
✓ Branch 19 → 20 taken 5 times.
✓ Branch 19 → 22 taken 42 times.
47 if (!cardView || keys.size() * 2 > qMax(int(m_rows.size()), 1) || !ensureReader()) {
312 5 restartProjection();
313 5 return;
314 }
315
5/5
✓ Branch 24 → 25 taken 42 times.
✓ Branch 25 → 26 taken 42 times.
✓ Branch 26 → 27 taken 42 times.
✓ Branch 109 → 28 taken 45 times.
✓ Branch 109 → 110 taken 42 times.
87 for (const QString &key : QSet<QString>(keys.cbegin(), keys.cend())) {
316 45 qint64 bookId = 0;
317 45 QString uid;
318
2/3
✓ Branch 29 → 30 taken 45 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 45 times.
45 if (!ContactQueries::splitKey(key, &bookId, &uid))
319 ✗ continue;
320 45 QList<ContactQueries::Row> fresh;
321
3/3
✓ Branch 33 → 34 taken 45 times.
✓ Branch 35 → 36 taken 36 times.
✓ Branch 35 → 46 taken 9 times.
45 if (const auto stored = m_reader->card(bookId, uid))
322
3/3
✓ Branch 37 → 38 taken 36 times.
✓ Branch 39 → 40 taken 36 times.
✓ Branch 40 → 41 taken 36 times.
45 fresh = ContactQueries::rowsForCards(m_reader->database(), m_view, m_filter, {stored->id});
323 45 const int existing = indexOfKey(key);
324
2/2
✓ Branch 49 → 50 taken 9 times.
✓ Branch 49 → 56 taken 36 times.
45 if (fresh.isEmpty()) {
325
1/2
✓ Branch 50 → 51 taken 9 times.
✗ Branch 50 → 55 not taken.
9 if (existing >= 0) {
326
1/1
✓ Branch 52 → 53 taken 9 times.
9 beginRemoveRows({}, existing, existing);
327
1/1
✓ Branch 53 → 54 taken 9 times.
9 m_rows.removeAt(existing);
328
1/1
✓ Branch 54 → 55 taken 9 times.
9 endRemoveRows();
329 }
330 9 continue;
331 }
332
1/1
✓ Branch 56 → 57 taken 36 times.
36 const ContactQueries::Row row = fresh.first();
333
6/6
✓ Branch 58 → 59 taken 21 times.
✓ Branch 58 → 63 taken 15 times.
✓ Branch 61 → 62 taken 17 times.
✓ Branch 61 → 63 taken 4 times.
✓ Branch 64 → 65 taken 17 times.
✓ Branch 64 → 75 taken 19 times.
36 if (existing >= 0 && m_rows.at(existing).sortKey == row.sortKey) {
334
1/1
✓ Branch 65 → 66 taken 17 times.
17 m_rows[existing] = row;
335
3/3
✓ Branch 69 → 70 taken 17 times.
✓ Branch 71 → 72 taken 17 times.
✓ Branch 72 → 73 taken 17 times.
17 emit dataChanged(index(existing), index(existing));
336 17 continue;
337 }
338
2/2
✓ Branch 75 → 76 taken 4 times.
✓ Branch 75 → 80 taken 15 times.
19 if (existing >= 0) {
339
1/1
✓ Branch 77 → 78 taken 4 times.
4 beginRemoveRows({}, existing, existing);
340
1/1
✓ Branch 78 → 79 taken 4 times.
4 m_rows.removeAt(existing);
341
1/1
✓ Branch 79 → 80 taken 4 times.
4 endRemoveRows();
342 }
343
1/1
✓ Branch 80 → 81 taken 19 times.
19 const int position = insertPosition(row);
344 // Rows beyond the loaded range arrive with the next page.
345
6/6
✓ Branch 82 → 83 taken 3 times.
✓ Branch 82 → 84 taken 16 times.
✓ Branch 83 → 84 taken 1 time.
✓ Branch 83 → 85 taken 2 times.
✓ Branch 86 → 87 taken 17 times.
✓ Branch 86 → 93 taken 2 times.
19 if (position < m_rows.size() || !m_hasMore) {
346
1/1
✓ Branch 88 → 89 taken 17 times.
17 beginInsertRows({}, position, position);
347
1/1
✓ Branch 89 → 90 taken 17 times.
17 m_rows.insert(position, row);
348
1/1
✓ Branch 90 → 91 taken 17 times.
17 endInsertRows();
349
2/2
✓ Branch 91 → 92 taken 4 times.
✓ Branch 91 → 93 taken 13 times.
17 if (existing >= 0)
350
1/1
✓ Branch 92 → 93 taken 4 times.
4 emit cardMoved(key, position);
351 }
352 130 }
353 42 refreshCount();
354 }
355
356 64 void QuickContactModel::restartProjection() {
357 // A restart returns to the first keyset page; writes also invalidate any
358 // visible completion answer.
359 64 beginResetModel();
360 64 m_rows.clear();
361 64 m_totalCount = 0;
362 64 m_hasMore = false;
363 64 endResetModel();
364 64 emit totalCountChanged();
365 64 ++m_completionGeneration;
366
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 64 times.
64 if (!m_completions.isEmpty()) {
367 ✗ m_completions.clear();
368 ✗ emit completionsChanged();
369 }
370 64 m_loading = false;
371 64 loadPage();
372 64 }
373
374 931 void QuickContactModel::loadPage() {
375
3/6
✓ Branch 3 → 4 taken 931 times.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 931 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 931 times.
931 if (m_databasePath.isEmpty() || m_loading)
376 ✗ return;
377 931 const quint64 generation = ++m_generation;
378 931 const QString filter = m_filter;
379 931 const QString databasePath = m_databasePath;
380 931 const ContactQueries::View view = m_view;
381 931 const bool first = m_rows.isEmpty();
382
3/3
✓ Branch 13 → 14 taken 918 times.
✓ Branch 13 → 15 taken 13 times.
✓ Branch 15 → 16 taken 13 times.
931 const QString afterKey = first ? QString() : m_rows.last().sortKey;
383
3/3
✓ Branch 17 → 18 taken 918 times.
✓ Branch 17 → 19 taken 13 times.
✓ Branch 19 → 20 taken 13 times.
931 const qint64 afterId = first ? 0 : m_rows.last().cardId;
384 // F-61: equal-name collected rows resume by their stable key.
385
3/3
✓ Branch 21 → 22 taken 918 times.
✓ Branch 21 → 23 taken 13 times.
✓ Branch 23 → 24 taken 13 times.
931 const QString afterRowKey = first ? QString() : m_rows.last().key;
386 931 m_loading = true;
387
1/1
✓ Branch 25 → 26 taken 931 times.
931 emit loadingChanged();
388
1/9
✓ Branch 31 → 32 taken 931 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 50 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
931 m_pool.start([this, generation, filter, databasePath, view, first, afterKey, afterId, afterRowKey] {
389 931 QList<ContactQueries::Row> page;
390 931 int total = -1;
391
3/4
✓ Branch 2 → 3 taken 931 times.
✓ Branch 3 → 4 taken 931 times.
✓ Branch 4 → 5 taken 931 times.
✗ Branch 4 → 21 not taken.
931 if (ContactStore store; store.open(databasePath)) {
392
2/2
✓ Branch 5 → 6 taken 931 times.
✓ Branch 6 → 7 taken 931 times.
931 page = ContactQueries::page(store.database(), view, filter, PageSize, afterKey, afterId, afterRowKey);
393 // A first page that is not full is the whole result: no COUNT scan.
394
2/2
✓ Branch 10 → 11 taken 918 times.
✓ Branch 10 → 21 taken 13 times.
931 if (first)
395
6/8
✓ Branch 12 → 13 taken 892 times.
✓ Branch 12 → 15 taken 26 times.
✓ Branch 15 → 16 taken 26 times.
✓ Branch 16 → 17 taken 26 times.
✓ Branch 18 → 19 taken 26 times.
✓ Branch 18 → 20 taken 892 times.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
918 total = page.size() < PageSize ? int(page.size()) : ContactQueries::count(store.database(), view, filter);
396 931 }
397 931 QMetaObject::invokeMethod(
398 this,
399
1/3
✓ Branch 24 → 25 taken 931 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
931 [this, generation, page = std::move(page), total]() mutable {
400 // A newer request (view/filter change, restart) owns the
401 // projection; its own answer arrives later.
402
2/2
✓ Branch 2 → 3 taken 7 times.
✓ Branch 2 → 4 taken 798 times.
805 if (generation != m_generation)
403 7 return;
404 798 m_loading = false;
405
2/2
✓ Branch 5 → 6 taken 245 times.
✓ Branch 5 → 12 taken 553 times.
798 if (!page.isEmpty()) {
406 245 const int start = int(m_rows.size());
407
1/1
✓ Branch 9 → 10 taken 245 times.
245 beginInsertRows({}, start, start + int(page.size()) - 1);
408 245 m_rows.append(page);
409 245 endInsertRows();
410 }
411
2/2
✓ Branch 12 → 13 taken 785 times.
✓ Branch 12 → 14 taken 13 times.
798 if (total >= 0)
412 785 m_totalCount = total;
413 798 m_hasMore = page.size() == PageSize;
414 798 emit loadingChanged();
415 798 emit totalCountChanged();
416 },
417 Qt::QueuedConnection);
418 931 });
419 931 }
420
421 114 void QuickContactModel::requestCompletions(const QString &fragment) {
422 114 const quint64 generation = ++m_completionGeneration;
423
2/2
✓ Branch 2 → 3 taken 114 times.
✓ Branch 3 → 4 taken 114 times.
114 const QString trimmed = fragment.left(64).trimmed();
424
5/6
✓ Branch 6 → 7 taken 114 times.
✗ Branch 6 → 9 not taken.
✓ Branch 8 → 9 taken 91 times.
✓ Branch 8 → 10 taken 23 times.
✓ Branch 11 → 12 taken 91 times.
✓ Branch 11 → 17 taken 23 times.
114 if (m_databasePath.isEmpty() || trimmed.isEmpty()) {
425
2/2
✓ Branch 13 → 14 taken 10 times.
✓ Branch 13 → 16 taken 81 times.
91 if (!m_completions.isEmpty()) {
426
1/1
✓ Branch 14 → 15 taken 10 times.
10 m_completions.clear();
427
1/1
✓ Branch 15 → 16 taken 10 times.
10 emit completionsChanged();
428 }
429 91 return;
430 }
431 23 const QString databasePath = m_databasePath;
432 // P90.13: latest-first — a queued completion for an older fragment is
433 // dropped instead of opening the store again.
434
1/1
✓ Branch 18 → 19 taken 23 times.
23 m_completionPool.clear();
435
1/3
✓ Branch 21 → 22 taken 23 times.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
23 m_completionPool.start([this, generation, trimmed, databasePath] {
436 21 QVariantList results;
437
3/4
✓ Branch 2 → 3 taken 21 times.
✓ Branch 3 → 4 taken 21 times.
✓ Branch 4 → 5 taken 21 times.
✗ Branch 4 → 226 not taken.
21 if (ContactStore store; store.open(databasePath)) {
438
6/6
✓ Branch 5 → 6 taken 21 times.
✓ Branch 6 → 7 taken 21 times.
✓ Branch 8 → 9 taken 21 times.
✓ Branch 9 → 10 taken 21 times.
✓ Branch 223 → 11 taken 19 times.
✓ Branch 223 → 224 taken 21 times.
40 for (const ContactQueries::Completion &item : ContactQueries::completions(store.database(), trimmed, 8)) {
439
2/2
✓ Branch 12 → 13 taken 7 times.
✓ Branch 12 → 116 taken 12 times.
19 if (item.group) {
440 // S97.5.4: a group expands into its members on accept.
441 7 const int count = int(item.members.size());
442
1/1
✓ Branch 76 → 77 taken 7 times.
7 results.append(QVariantMap{
443 14 {QStringLiteral("key"), item.key},
444 14 {QStringLiteral("name"), item.name},
445 14 {QStringLiteral("email"), QString()},
446
1/1
✓ Branch 33 → 34 taken 7 times.
21 {QStringLiteral("insert"), item.members.join(QStringLiteral(", "))},
447
4/6
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 42 taken 6 times.
✓ Branch 39 → 40 taken 1 time.
✓ Branch 41 → 45 taken 1 time.
✗ Branch 295 → 296 not taken.
✗ Branch 295 → 297 not taken.
20 {QStringLiteral("label"), count == 1 ? tr("%1 (1 person)").arg(item.name)
448
6/8
✓ Branch 42 → 43 taken 6 times.
✓ Branch 44 → 45 taken 6 times.
✓ Branch 97 → 98 taken 6 times.
✓ Branch 97 → 99 taken 1 time.
✓ Branch 99 → 100 taken 1 time.
✓ Branch 99 → 101 taken 6 times.
✗ Branch 290 → 291 not taken.
✗ Branch 290 → 292 not taken.
19 : tr("%1 (%n people)", "", count).arg(item.name)},
449
4/4
✓ Branch 49 → 50 taken 1 time.
✓ Branch 49 → 51 taken 6 times.
✓ Branch 50 → 52 taken 1 time.
✓ Branch 51 → 52 taken 6 times.
14 {QStringLiteral("detail"), count == 1 ? tr("1 person") : tr("%n people", "", count)},
450 14 {QStringLiteral("book"), item.bookName},
451 14 {QStringLiteral("collected"), false},
452 14 {QStringLiteral("group"), true},
453 14 {QStringLiteral("initials"), QString()},
454
2/4
✓ Branch 80 → 81 taken 77 times.
✓ Branch 80 → 82 taken 7 times.
✗ Branch 240 → 241 not taken.
✗ Branch 240 → 242 not taken.
91 {QStringLiteral("avatarColor"), item.avatarColor}});
455 7 continue;
456 7 }
457
2/4
✓ Branch 117 → 118 taken 12 times.
✗ Branch 117 → 121 not taken.
✓ Branch 119 → 120 taken 12 times.
✗ Branch 119 → 121 not taken.
12 const bool named = !item.name.isEmpty() && item.name != item.address;
458
1/1
✓ Branch 184 → 185 taken 12 times.
12 results.append(QVariantMap{
459 24 {QStringLiteral("key"), item.key},
460 24 {QStringLiteral("name"), item.name},
461 24 {QStringLiteral("email"), item.address},
462
2/3
✓ Branch 137 → 138 taken 12 times.
✗ Branch 137 → 139 not taken.
✓ Branch 138 → 140 taken 12 times.
24 {QStringLiteral("insert"), named ? MailboxText::mailbox(item.name, item.address) : item.address},
463
4/11
✓ Branch 144 → 145 taken 12 times.
✗ Branch 144 → 149 not taken.
✓ Branch 148 → 150 taken 12 times.
✓ Branch 205 → 206 taken 12 times.
✗ Branch 205 → 207 not taken.
✓ Branch 207 → 208 taken 12 times.
✗ Branch 207 → 209 not taken.
✗ Branch 403 → 404 not taken.
✗ Branch 403 → 405 not taken.
✗ Branch 407 → 408 not taken.
✗ Branch 407 → 409 not taken.
36 {QStringLiteral("label"), named ? QStringLiteral("%1 · %2").arg(item.name, item.address) : item.address},
464 24 {QStringLiteral("book"), item.bookName},
465 24 {QStringLiteral("collected"), item.collected},
466 24 {QStringLiteral("group"), false},
467
3/3
✓ Branch 166 → 167 taken 3 times.
✓ Branch 166 → 168 taken 9 times.
✓ Branch 167 → 169 taken 3 times.
24 {QStringLiteral("detail"), item.collected ? tr("Collected") : item.bookName},
468
2/3
✓ Branch 173 → 174 taken 12 times.
✗ Branch 173 → 175 not taken.
✓ Branch 176 → 177 taken 12 times.
24 {QStringLiteral("initials"), initialsOf(named ? item.name : item.address)},
469
2/4
✓ Branch 188 → 189 taken 132 times.
✓ Branch 188 → 190 taken 12 times.
✗ Branch 355 → 356 not taken.
✗ Branch 355 → 357 not taken.
168 {QStringLiteral("avatarColor"), item.avatarColor}});
470 21 }
471 21 }
472
1/1
✓ Branch 228 → 229 taken 21 times.
21 QMetaObject::invokeMethod(
473 this,
474 42 [this, generation, results]() {
475
2/2
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 4 taken 17 times.
21 if (generation != m_completionGeneration)
476 4 return; // stale answer
477 17 m_completions = results;
478 17 emit completionsChanged();
479 },
480 Qt::QueuedConnection);
481
2/10
✓ Branch 74 → 75 taken 7 times.
✓ Branch 182 → 183 taken 12 times.
✗ Branch 342 → 343 not taken.
✗ Branch 342 → 346 not taken.
✗ Branch 344 → 345 not taken.
✗ Branch 344 → 346 not taken.
✗ Branch 445 → 446 not taken.
✗ Branch 445 → 449 not taken.
✗ Branch 447 → 448 not taken.
✗ Branch 447 → 449 not taken.
275 });
482 114 }
483