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: 97.2% 208 / 0 / 214
Functions: 100.0% 26 / 0 / 26
Branches: 90.1% 326 / 0 / 362

presentation/FolderPresentationModel.cpp
Line Branch Exec Source
1 #include "FolderPresentationModel.h"
2 #include "service/ImapResponseParser.h"
3 #include <algorithm>
4
5 35151 FolderPresentationModel::Node *FolderPresentationModel::node(const QModelIndex &index) const {
6
3/4
✓ Branch 3 → 4 taken 35112 times.
✓ Branch 3 → 7 taken 39 times.
✓ Branch 5 → 6 taken 35112 times.
✗ Branch 5 → 7 not taken.
35151 return index.isValid() && index.model() == this ? static_cast<Node *>(index.internalPointer()) : nullptr;
7 }
8
9 18022 QModelIndex FolderPresentationModel::index(int row, int column, const QModelIndex &parent) const {
10
6/10
✓ Branch 2 → 3 taken 18022 times.
✗ Branch 2 → 8 not taken.
✓ Branch 3 → 4 taken 18022 times.
✗ Branch 3 → 8 not taken.
✓ Branch 5 → 6 taken 287 times.
✓ Branch 5 → 9 taken 17735 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 287 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 18022 times.
18022 if (column != 0 || row < 0 || (parent.isValid() && parent.column() != 0)) return {};
11
2/2
✓ Branch 13 → 14 taken 287 times.
✓ Branch 13 → 15 taken 17735 times.
18022 const auto *owner = parent.isValid() ? node(parent) : &m_root;
12
5/6
✓ Branch 16 → 17 taken 18022 times.
✗ Branch 16 → 19 not taken.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 20 taken 18021 times.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 23 taken 18021 times.
18022 if (!owner || row >= owner->children.size()) return {};
13 18021 return createIndex(row, column, owner->children[row]);
14 }
15
16 20624 QModelIndex FolderPresentationModel::parent(const QModelIndex &index) const {
17 20624 const auto *child = node(index);
18
5/6
✓ Branch 3 → 4 taken 20598 times.
✓ Branch 3 → 6 taken 26 times.
✓ Branch 4 → 5 taken 897 times.
✓ Branch 4 → 6 taken 19701 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 897 times.
20624 if (!child || child->parent == &m_root || !child->parent) return {};
19 897 return createIndex(child->parent->row, 0, child->parent);
20 }
21
22 4990 int FolderPresentationModel::rowCount(const QModelIndex &parent) const {
23
4/6
✓ Branch 3 → 4 taken 2417 times.
✓ Branch 3 → 7 taken 2573 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2417 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 4990 times.
4990 if (parent.isValid() && parent.column() != 0) return 0;
24
2/2
✓ Branch 11 → 12 taken 2417 times.
✓ Branch 11 → 13 taken 2573 times.
4990 const auto *owner = parent.isValid() ? node(parent) : &m_root;
25
1/2
✓ Branch 14 → 15 taken 4990 times.
✗ Branch 14 → 17 not taken.
4990 return owner ? owner->children.size() : 0;
26 }
27
28 24109 int FolderPresentationModel::columnCount(const QModelIndex &) const { return 1; }
29
30 44 Qt::ItemFlags FolderPresentationModel::flags(const QModelIndex &index) const {
31 44 const auto *item = node(index);
32
2/2
✓ Branch 3 → 4 taken 13 times.
✓ Branch 3 → 7 taken 31 times.
44 if (!item) return Qt::NoItemFlags;
33
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 24 times.
24 return Qt::ItemIsEnabled | ((!item->synthetic && !item->folder.isNoselect())
34
2/2
✓ Branch 7 → 8 taken 24 times.
✓ Branch 7 → 11 taken 7 times.
55 ? Qt::ItemIsSelectable : Qt::NoItemFlags);
35 }
36
37 11779 QVariant FolderPresentationModel::data(const QModelIndex &index, int role) const {
38 11779 const auto *item = node(index);
39
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 11779 times.
11779 if (!item) return {};
40
8/9
✓ Branch 5 → 6 taken 1875 times.
✓ Branch 5 → 7 taken 1864 times.
✓ Branch 5 → 8 taken 1852 times.
✓ Branch 5 → 14 taken 1937 times.
✓ Branch 5 → 15 taken 1 time.
✗ Branch 5 → 22 not taken.
✓ Branch 5 → 23 taken 2078 times.
✓ Branch 5 → 34 taken 2076 times.
✓ Branch 5 → 38 taken 96 times.
11779 switch (role) {
41 1875 case PathRole: return item->folder.path;
42 1864 case Qt::DisplayRole:
43 1864 case NameRole: return item->folder.name;
44
4/4
✓ Branch 8 → 9 taken 1837 times.
✓ Branch 8 → 12 taken 15 times.
✓ Branch 10 → 11 taken 1801 times.
✓ Branch 10 → 12 taken 36 times.
1852 case SelectableRole: return !item->synthetic && !item->folder.isNoselect();
45 1937 case UnreadRole: return item->unread;
46 2 case KindRole: return QStringLiteral("mailbox");
47 ✗ case ResultCountRole: return -1;
48 2078 case IconRole: {
49 2078 const auto it = m_appearance.constFind(item->folder.path);
50
4/4
✓ Branch 26 → 27 taken 2058 times.
✓ Branch 26 → 31 taken 20 times.
✓ Branch 28 → 29 taken 2057 times.
✓ Branch 28 → 31 taken 1 time.
2078 return it != m_appearance.constEnd() && it->glyph > 0 ? it->glyph : m_fallbackGlyph;
51 }
52 2076 case ColorRole: return m_appearance.value(item->folder.path).color;
53 96 default: return {};
54 }
55 }
56
57 722 QHash<int, QByteArray> FolderPresentationModel::roleNames() const {
58 ✗ return {{PathRole, "folderPath"}, {NameRole, "folderName"},
59 ✗ {SelectableRole, "selectable"}, {UnreadRole, "unreadCount"},
60 ✗ {KindRole, "nodeKind"}, {ResultCountRole, "resultCount"},
61
2/4
✓ Branch 12 → 13 taken 5776 times.
✓ Branch 12 → 14 taken 722 times.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
6498 {IconRole, "folderGlyph"}, {ColorRole, "folderColor"}};
62 722 }
63
64 7366 FolderPresentationModel::Node *FolderPresentationModel::ensureNode(const QString &path,
65 const QString &delimiter) {
66 7366 QStringList missing;
67 7366 auto currentPath = path;
68 // Iterative construction handles deep namespaces without recursive stack growth.
69
6/6
✓ Branch 17 → 18 taken 12579 times.
✓ Branch 17 → 21 taken 2266 times.
✓ Branch 19 → 20 taken 7479 times.
✓ Branch 19 → 21 taken 5100 times.
✓ Branch 22 → 4 taken 7479 times.
✓ Branch 22 → 23 taken 7366 times.
14845 while (!currentPath.isEmpty() && !m_paths.contains(currentPath)) {
70
1/1
✓ Branch 4 → 5 taken 7479 times.
7479 missing.prepend(currentPath);
71
3/3
✓ Branch 6 → 7 taken 1832 times.
✓ Branch 6 → 8 taken 5647 times.
✓ Branch 8 → 9 taken 5647 times.
7479 const int split = delimiter.isEmpty() ? -1 : currentPath.lastIndexOf(delimiter);
72
3/3
✓ Branch 10 → 11 taken 5213 times.
✓ Branch 10 → 12 taken 2266 times.
✓ Branch 11 → 13 taken 5213 times.
7479 currentPath = split > 0 ? currentPath.left(split) : QString{};
73 }
74 7366 auto *owner = m_paths.value(currentPath, &m_root);
75
4/4
✓ Branch 24 → 25 taken 7366 times.
✓ Branch 25 → 26 taken 7366 times.
✓ Branch 52 → 27 taken 7479 times.
✓ Branch 52 → 53 taken 7366 times.
14845 for (const auto &missingPath : missing) {
76
1/1
✓ Branch 28 → 29 taken 7479 times.
7479 auto created = std::make_unique<Node>();
77 7479 created->folder.path = missingPath;
78 7479 created->folder.delimiter = delimiter;
79
3/3
✓ Branch 34 → 35 taken 1832 times.
✓ Branch 34 → 36 taken 5647 times.
✓ Branch 37 → 38 taken 5647 times.
13126 const auto leaf = delimiter.isEmpty() ? missingPath : missingPath.section(delimiter, -1);
80
1/1
✓ Branch 38 → 39 taken 7479 times.
7479 created->folder.name = ImapResponseParser::decodeMailboxName(leaf);
81 7479 created->parent = owner;
82 7479 auto *item = created.get();
83
1/1
✓ Branch 44 → 45 taken 7479 times.
7479 owner->children.append(item);
84
1/1
✓ Branch 45 → 46 taken 7479 times.
7479 m_paths.insert(missingPath, item);
85
1/1
✓ Branch 47 → 48 taken 7479 times.
7479 m_nodes.push_back(std::move(created));
86 7479 owner = item;
87 7479 }
88 7366 return owner;
89 7366 }
90
91 834 void FolderPresentationModel::setFolders(const QList<FolderInfo> &folders,
92 const QStringList &hidden) {
93
1/1
✓ Branch 2 → 3 taken 834 times.
834 beginResetModel();
94
1/1
✓ Branch 3 → 4 taken 834 times.
834 m_root.children.clear();
95 834 m_paths.clear();
96 834 m_nodes.clear();
97
1/1
✓ Branch 6 → 7 taken 834 times.
834 m_selectablePaths.clear();
98
1/1
✓ Branch 7 → 8 taken 834 times.
834 m_completionIndex.clear();
99 834 m_completionIndexValid = false;
100
2/2
✓ Branch 44 → 10 taken 7371 times.
✓ Branch 44 → 45 taken 834 times.
8205 for (const auto &folder : folders) {
101
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 7371 times.
7376 if (folder.path.isEmpty()) continue;
102 7371 auto *existing = m_paths.value(folder.path);
103
4/4
✓ Branch 15 → 16 taken 3 times.
✓ Branch 15 → 18 taken 7368 times.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 18 taken 1 time.
7371 if (existing && !existing->synthetic) continue;
104
1/1
✓ Branch 20 → 21 taken 7369 times.
7369 const bool hide = std::any_of(hidden.begin(), hidden.end(), [&](const QString &path) {
105
4/6
✓ Branch 3 → 4 taken 7 times.
✓ Branch 3 → 11 taken 1 time.
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 12 not taken.
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 12 not taken.
15 return folder.path == path || (!path.isEmpty() && !folder.delimiter.isEmpty() &&
106
6/8
✓ Branch 8 → 9 taken 7 times.
✓ Branch 9 → 10 taken 7 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 12 taken 5 times.
✓ Branch 13 → 14 taken 7 times.
✓ Branch 13 → 15 taken 1 time.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
15 folder.path.startsWith(path + folder.delimiter));
107 });
108
2/2
✓ Branch 21 → 22 taken 3 times.
✓ Branch 21 → 23 taken 7366 times.
7369 if (hide) continue;
109
1/1
✓ Branch 23 → 24 taken 7366 times.
7366 auto *item = ensureNode(folder.path, folder.delimiter);
110 7366 const auto fallbackName = item->folder.name;
111 7366 item->folder = folder;
112
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 7366 times.
20266 item->folder.name = folder.name.isEmpty() ? fallbackName : folder.delimiter.isEmpty()
113
3/3
✓ Branch 30 → 31 taken 1832 times.
✓ Branch 30 → 32 taken 5534 times.
✓ Branch 33 → 34 taken 5534 times.
12900 ? folder.name : folder.name.section(folder.delimiter, -1);
114 7366 item->synthetic = false;
115
4/4
✓ Branch 36 → 37 taken 7366 times.
✓ Branch 37 → 38 taken 7352 times.
✓ Branch 37 → 39 taken 14 times.
✓ Branch 38 → 39 taken 7352 times.
7366 if (!folder.isNoselect()) m_selectablePaths.append(folder.path);
116 7366 }
117 8313 auto sortChildren = [](Node *owner) {
118 8313 std::sort(owner->children.begin(), owner->children.end(), [](const Node *left, const Node *right) {
119
2/2
✓ Branch 4 → 5 taken 1164 times.
✓ Branch 4 → 7 taken 43495 times.
44659 if ((left->folder.path == "INBOX") != (right->folder.path == "INBOX")) return left->folder.path == "INBOX";
120 43495 const int order = QString::compare(left->folder.name, right->folder.name, Qt::CaseInsensitive);
121
1/2
✓ Branch 8 → 9 taken 43495 times.
✗ Branch 8 → 10 not taken.
43495 return order != 0 ? order < 0 : left->folder.path < right->folder.path;
122 });
123
2/2
✓ Branch 9 → 6 taken 7479 times.
✓ Branch 9 → 10 taken 8313 times.
15792 for (int row = 0; row < owner->children.size(); ++row) owner->children[row]->row = row;
124 8313 };
125
1/1
✓ Branch 45 → 46 taken 834 times.
834 sortChildren(&m_root);
126
3/3
✓ Branch 50 → 51 taken 7479 times.
✓ Branch 53 → 48 taken 7479 times.
✓ Branch 53 → 54 taken 834 times.
8313 for (const auto &item : m_nodes) sortChildren(item.get());
127
1/1
✓ Branch 54 → 55 taken 834 times.
834 endResetModel();
128 834 }
129
130 34 QStringList FolderPresentationModel::branchPaths() const {
131 34 QStringList result;
132
5/5
✓ Branch 7 → 8 taken 77 times.
✓ Branch 7 → 10 taken 215 times.
✓ Branch 9 → 10 taken 77 times.
✓ Branch 12 → 4 taken 292 times.
✓ Branch 12 → 13 taken 34 times.
326 for (const auto &item : m_nodes) if (!item->children.isEmpty()) result.append(item->folder.path);
133 34 return result;
134 ✗ }
135
136 1324 QModelIndex FolderPresentationModel::indexForPath(const QString &path) const {
137 1324 const auto *item = m_paths.value(path);
138
2/2
✓ Branch 3 → 4 taken 1162 times.
✓ Branch 3 → 5 taken 162 times.
1324 return item ? createIndex(item->row, 0, item) : QModelIndex{};
139 }
140
141 123 void FolderPresentationModel::setUnread(const QString &path, int unread) {
142 123 auto *item = m_paths.value(path);
143 123 const int count = std::max(0, unread);
144
6/6
✓ Branch 4 → 5 taken 115 times.
✓ Branch 4 → 7 taken 8 times.
✓ Branch 5 → 6 taken 114 times.
✓ Branch 5 → 7 taken 1 time.
✓ Branch 6 → 7 taken 16 times.
✓ Branch 6 → 8 taken 98 times.
123 if (!item || item->synthetic || item->unread == count) return;
145 98 item->unread = count;
146 98 const auto changed = indexForPath(path);
147
2/2
✓ Branch 9 → 10 taken 98 times.
✓ Branch 10 → 11 taken 98 times.
98 emit dataChanged(changed, changed, {UnreadRole});
148 }
149
150 13066 bool FolderPresentationModel::isSelectable(const QString &path) const {
151 13066 const auto *item = m_paths.value(path);
152
6/6
✓ Branch 3 → 4 taken 7915 times.
✓ Branch 3 → 8 taken 5151 times.
✓ Branch 4 → 5 taken 7914 times.
✓ Branch 4 → 8 taken 1 time.
✓ Branch 6 → 7 taken 7906 times.
✓ Branch 6 → 8 taken 8 times.
13066 return item && !item->synthetic && !item->folder.isNoselect();
153 }
154
155 516 QList<FolderPresentationModel::TreeRow> FolderPresentationModel::treeRows() const {
156 516 QList<TreeRow> rows;
157 516 QList<QPair<const Node *, int>> stack;
158
6/6
✓ Branch 3 → 4 taken 1534 times.
✓ Branch 5 → 6 taken 1534 times.
✓ Branch 6 → 7 taken 1534 times.
✓ Branch 8 → 9 taken 2050 times.
✓ Branch 9 → 3 taken 1534 times.
✓ Branch 9 → 10 taken 516 times.
2050 for (auto it = m_root.children.crbegin(); it != m_root.children.crend(); ++it) stack.append({*it, 0});
159
2/2
✓ Branch 28 → 11 taken 1558 times.
✓ Branch 28 → 29 taken 516 times.
2074 while (!stack.isEmpty()) {
160
1/1
✓ Branch 11 → 12 taken 1558 times.
1558 const auto [node, depth] = stack.takeLast();
161
6/6
✓ Branch 15 → 16 taken 24 times.
✓ Branch 17 → 18 taken 24 times.
✓ Branch 18 → 19 taken 24 times.
✓ Branch 20 → 21 taken 1582 times.
✓ Branch 21 → 15 taken 24 times.
✓ Branch 21 → 22 taken 1558 times.
1582 for (auto it = node->children.crbegin(); it != node->children.crend(); ++it) stack.append({*it, depth + 1});
162 1558 rows.append({node->folder.path, node->folder.name, depth});
163 }
164 516 return rows;
165 2074 }
166
167 11 QStringList FolderPresentationModel::matchingPaths(const QString &prefix, int limit) const {
168 11 QStringList result;
169
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 10 times.
11 if (limit <= 0) return result;
170
2/2
✓ Branch 21 → 6 taken 20 times.
✓ Branch 21 → 22 taken 8 times.
28 for (const auto &path : m_selectablePaths) {
171
5/5
✓ Branch 7 → 8 taken 20 times.
✓ Branch 8 → 9 taken 10 times.
✓ Branch 8 → 12 taken 10 times.
✓ Branch 14 → 15 taken 11 times.
✓ Branch 14 → 19 taken 9 times.
30 if (path.contains(prefix, Qt::CaseInsensitive) ||
172
3/3
✓ Branch 10 → 11 taken 10 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 9 times.
10 m_paths.value(path)->folder.name.contains(prefix, Qt::CaseInsensitive)) {
173
1/1
✓ Branch 15 → 16 taken 11 times.
11 result.append(path);
174
2/2
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 9 times.
11 if (result.size() >= limit) break;
175 }
176 }
177 10 return result;
178 ✗ }
179
180 namespace {
181 // Localized names of the special folders (D-20): the words a user types for
182 // them, matched as the fourth class.
183 5266 QString specialNames(const FolderInfo &folder) {
184
1/1
✓ Branch 2 → 3 taken 5266 times.
5266 const QString path = folder.path.toLower();
185
2/2
✓ Branch 5 → 6 taken 40 times.
✓ Branch 5 → 11 taken 5226 times.
5306 if (folder.path.compare(QLatin1String("INBOX"), Qt::CaseInsensitive) == 0) return QStringLiteral("posteingang inbox");
186
7/8
✓ Branch 11 → 12 taken 5226 times.
✓ Branch 12 → 13 taken 5223 times.
✓ Branch 12 → 16 taken 3 times.
✓ Branch 14 → 15 taken 5223 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 5223 times.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 24 taken 5223 times.
5229 if (folder.isSent() || path.endsWith(QLatin1String("sent"))) return QStringLiteral("gesendet gesendete sent");
187
7/8
✓ Branch 24 → 25 taken 5223 times.
✓ Branch 25 → 26 taken 5220 times.
✓ Branch 25 → 29 taken 3 times.
✓ Branch 27 → 28 taken 5220 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 5220 times.
✓ Branch 31 → 32 taken 3 times.
✓ Branch 31 → 37 taken 5220 times.
5226 if (folder.isDrafts() || path.endsWith(QLatin1String("drafts"))) return QStringLiteral("entwürfe drafts");
188
7/8
✓ Branch 37 → 38 taken 5220 times.
✓ Branch 38 → 39 taken 5209 times.
✓ Branch 38 → 42 taken 11 times.
✓ Branch 40 → 41 taken 5209 times.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 5209 times.
✓ Branch 44 → 45 taken 11 times.
✓ Branch 44 → 50 taken 5209 times.
5231 if (folder.isTrash() || path.endsWith(QLatin1String("trash"))) return QStringLiteral("papierkorb gelöscht trash");
189
9/11
✓ Branch 50 → 51 taken 5209 times.
✓ Branch 51 → 52 taken 5198 times.
✓ Branch 51 → 58 taken 11 times.
✓ Branch 53 → 54 taken 5198 times.
✓ Branch 54 → 55 taken 5198 times.
✗ Branch 54 → 58 not taken.
✓ Branch 56 → 57 taken 5198 times.
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 5198 times.
✓ Branch 60 → 61 taken 11 times.
✓ Branch 60 → 66 taken 5198 times.
5209 if (folder.isJunk() || path.endsWith(QLatin1String("junk")) || path.endsWith(QLatin1String("spam")))
190 11 return QStringLiteral("spam junk werbung");
191
8/8
✓ Branch 66 → 67 taken 5198 times.
✓ Branch 67 → 68 taken 5196 times.
✓ Branch 67 → 71 taken 2 times.
✓ Branch 69 → 70 taken 5196 times.
✓ Branch 70 → 71 taken 19 times.
✓ Branch 70 → 72 taken 5177 times.
✓ Branch 73 → 74 taken 21 times.
✓ Branch 73 → 79 taken 5177 times.
5219 if (folder.isArchive() || path.endsWith(QLatin1String("archive"))) return QStringLiteral("archiv archive");
192 5177 return {};
193 5266 }
194 } // namespace
195
196 42 void FolderPresentationModel::buildCompletionIndex() const {
197
1/1
✓ Branch 2 → 3 taken 42 times.
42 m_completionIndex.clear();
198 // Tree order: depth first from the root, the order the folder tree shows.
199 42 QList<const Node *> stack;
200
6/6
✓ Branch 4 → 5 taken 213 times.
✓ Branch 5 → 6 taken 213 times.
✓ Branch 6 → 7 taken 213 times.
✓ Branch 8 → 9 taken 255 times.
✓ Branch 9 → 4 taken 213 times.
✓ Branch 9 → 10 taken 42 times.
255 for (auto it = m_root.children.crbegin(); it != m_root.children.crend(); ++it) stack.append(*it);
201
2/2
✓ Branch 70 → 11 taken 5369 times.
✓ Branch 70 → 71 taken 42 times.
5411 while (!stack.isEmpty()) {
202
1/1
✓ Branch 11 → 12 taken 5369 times.
5369 const Node *node = stack.takeLast();
203
6/6
✓ Branch 13 → 14 taken 5156 times.
✓ Branch 14 → 15 taken 5156 times.
✓ Branch 15 → 16 taken 5156 times.
✓ Branch 17 → 18 taken 10525 times.
✓ Branch 18 → 13 taken 5156 times.
✓ Branch 18 → 19 taken 5369 times.
10525 for (auto it = node->children.crbegin(); it != node->children.crend(); ++it) stack.append(*it);
204
6/7
✓ Branch 19 → 20 taken 5266 times.
✓ Branch 19 → 22 taken 103 times.
✓ Branch 20 → 21 taken 5266 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 5266 times.
✓ Branch 24 → 25 taken 103 times.
✓ Branch 24 → 26 taken 5266 times.
5369 if (node->synthetic || node->folder.isNoselect()) continue;
205 5266 QStringList segments;
206
4/5
✓ Branch 27 → 28 taken 10056 times.
✓ Branch 29 → 30 taken 15322 times.
✗ Branch 29 → 31 not taken.
✓ Branch 30 → 27 taken 10056 times.
✓ Branch 30 → 31 taken 5266 times.
15322 for (const Node *item = node->parent; item && item != &m_root; item = item->parent) segments.prepend(item->folder.name);
207 5266 CompletionIndexEntry entry;
208 5266 entry.node = node;
209
1/1
✓ Branch 33 → 34 taken 5266 times.
5266 entry.parent = segments.join(QLatin1Char('/'));
210
6/8
✓ Branch 37 → 38 taken 210 times.
✓ Branch 37 → 39 taken 5056 times.
✓ Branch 41 → 42 taken 5056 times.
✓ Branch 42 → 43 taken 5056 times.
✓ Branch 45 → 46 taken 5056 times.
✓ Branch 45 → 47 taken 210 times.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
5266 entry.display = entry.parent.isEmpty() ? node->folder.name : entry.parent + QLatin1Char('/') + node->folder.name;
211
1/1
✓ Branch 47 → 48 taken 5266 times.
5266 entry.leafLower = node->folder.name.toLower();
212
1/1
✓ Branch 50 → 51 taken 5266 times.
5266 entry.displayLower = entry.display.toLower();
213
1/1
✓ Branch 53 → 54 taken 5266 times.
5266 entry.pathLower = node->folder.path.toLower();
214
2/2
✓ Branch 56 → 57 taken 5266 times.
✓ Branch 60 → 61 taken 5266 times.
5266 entry.special = specialNames(node->folder).split(QLatin1Char(' '), Qt::SkipEmptyParts);
215
1/1
✓ Branch 64 → 65 taken 5266 times.
5266 m_completionIndex.append(entry);
216 5266 }
217 42 m_completionIndexValid = true;
218 42 }
219
220 145 QList<FolderPresentationModel::Completion> FolderPresentationModel::completionCandidates(
221 const QString &text, const QStringList &preferred, const QStringList &recent, int limit) const {
222
3/3
✓ Branch 2 → 3 taken 42 times.
✓ Branch 2 → 4 taken 103 times.
✓ Branch 3 → 4 taken 42 times.
145 if (!m_completionIndexValid) buildCompletionIndex();
223
2/2
✓ Branch 4 → 5 taken 145 times.
✓ Branch 5 → 6 taken 145 times.
145 const QString needle = text.trimmed().toLower();
224
1/1
✓ Branch 9 → 10 taken 145 times.
145 const QString segment = QLatin1Char('/') + needle;
225 struct Ranked { int index; int match; int priority; };
226 145 QList<Ranked> ranked;
227
2/2
✓ Branch 74 → 11 taken 200616 times.
✓ Branch 74 → 75 taken 145 times.
200761 for (int index = 0; index < m_completionIndex.size(); ++index) {
228 200616 const CompletionIndexEntry &entry = m_completionIndex.at(index);
229 200616 int match = 0;
230
2/2
✓ Branch 13 → 14 taken 200433 times.
✓ Branch 13 → 58 taken 183 times.
200616 if (!needle.isEmpty()) {
231 // Most folders do not contain the text at all: one test rejects them.
232
1/1
✓ Branch 14 → 15 taken 200433 times.
200433 const bool inDisplay = entry.displayLower.contains(needle);
233
8/9
✓ Branch 15 → 16 taken 180377 times.
✓ Branch 15 → 21 taken 20056 times.
✓ Branch 17 → 18 taken 180229 times.
✓ Branch 17 → 21 taken 148 times.
✓ Branch 18 → 19 taken 180229 times.
✓ Branch 19 → 20 taken 180229 times.
✗ Branch 19 → 21 not taken.
✓ Branch 22 → 23 taken 180229 times.
✓ Branch 22 → 24 taken 20204 times.
200433 if (!inDisplay && entry.special.isEmpty() && !entry.pathLower.contains(needle)) continue;
234
2/2
✓ Branch 24 → 25 taken 148 times.
✓ Branch 24 → 26 taken 20056 times.
20204 if (!inDisplay) match = 4;
235
5/6
✓ Branch 27 → 28 taken 16176 times.
✓ Branch 27 → 30 taken 4028 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 16176 times.
✓ Branch 32 → 33 taken 4028 times.
✓ Branch 32 → 34 taken 16176 times.
20204 if (entry.leafLower == needle || entry.displayLower == needle) match = 0;
236
8/8
✓ Branch 34 → 35 taken 16176 times.
✓ Branch 35 → 36 taken 155 times.
✓ Branch 35 → 38 taken 16021 times.
✓ Branch 36 → 37 taken 155 times.
✓ Branch 37 → 38 taken 5 times.
✓ Branch 37 → 39 taken 150 times.
✓ Branch 40 → 41 taken 16026 times.
✓ Branch 40 → 42 taken 150 times.
16176 else if (entry.leafLower.startsWith(needle) || entry.displayLower.startsWith(needle)) match = 1;
237
3/3
✓ Branch 42 → 43 taken 150 times.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 45 taken 149 times.
150 else if (entry.displayLower.contains(segment)) match = 2;
238
3/3
✓ Branch 47 → 48 taken 149 times.
✓ Branch 48 → 49 taken 4 times.
✓ Branch 48 → 50 taken 145 times.
149 else if (std::any_of(entry.special.cbegin(), entry.special.cend(),
239 352 [&needle](const QString &word) { return word.startsWith(needle); })) match = 3;
240
6/7
✓ Branch 50 → 51 taken 144 times.
✓ Branch 50 → 53 taken 1 time.
✓ Branch 51 → 52 taken 144 times.
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 144 times.
✓ Branch 55 → 56 taken 1 time.
✓ Branch 55 → 57 taken 144 times.
145 else if (inDisplay || entry.pathLower.contains(needle)) match = 4;
241 144 else continue;
242 }
243 20243 const QString &path = entry.node->folder.path;
244
2/2
✓ Branch 59 → 60 taken 20205 times.
✓ Branch 59 → 61 taken 38 times.
20243 const int recentIndex = recent.isEmpty() ? -1 : int(recent.indexOf(path));
245
4/4
✓ Branch 64 → 65 taken 5 times.
✓ Branch 64 → 66 taken 20238 times.
✓ Branch 66 → 67 taken 10 times.
✓ Branch 66 → 68 taken 20228 times.
20243 const int priority = preferred.contains(path) ? 0 : recentIndex >= 0 ? 1 + recentIndex : 1000;
246
1/1
✓ Branch 70 → 71 taken 20243 times.
20243 ranked.append({index, match, priority});
247 }
248 // Index order is tree order, so a stable sort keeps it within a class.
249
3/3
✓ Branch 75 → 76 taken 145 times.
✓ Branch 76 → 77 taken 145 times.
✓ Branch 77 → 78 taken 145 times.
145 std::stable_sort(ranked.begin(), ranked.end(), [](const Ranked &a, const Ranked &b) {
250
2/2
✓ Branch 2 → 3 taken 12515 times.
✓ Branch 2 → 4 taken 104504 times.
117019 return a.match != b.match ? a.match < b.match : a.priority < b.priority;
251 });
252 145 QList<Completion> result;
253
4/4
✓ Branch 78 → 79 taken 145 times.
✓ Branch 79 → 80 taken 145 times.
✓ Branch 94 → 81 taken 2283 times.
✓ Branch 94 → 95 taken 105 times.
2388 for (const Ranked &entry : ranked) {
254
2/2
✓ Branch 83 → 84 taken 40 times.
✓ Branch 83 → 85 taken 2243 times.
2283 if (result.size() >= limit) break;
255 2243 const CompletionIndexEntry &item = m_completionIndex.at(entry.index);
256 2243 result.append({item.node->folder.path, item.node->folder.name, item.parent, item.node->unread,
257 2243 preferred.contains(item.node->folder.path)});
258 }
259 145 return result;
260 2388 }
261
262 799 QString FolderPresentationModel::firstSelectable() const {
263
7/9
✓ Branch 2 → 3 taken 799 times.
✓ Branch 3 → 4 taken 799 times.
✓ Branch 4 → 5 taken 591 times.
✓ Branch 4 → 8 taken 208 times.
✓ Branch 8 → 9 taken 208 times.
✓ Branch 10 → 11 taken 591 times.
✓ Branch 10 → 12 taken 208 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
1390 return isSelectable("INBOX") ? QStringLiteral("INBOX") : m_selectablePaths.value(0);
264 }
265
266 831 void FolderPresentationModel::setAppearance(const QHash<QString, Appearance> &byPath,
267 int fallbackGlyph) {
268 831 m_appearance = byPath;
269 831 m_fallbackGlyph = fallbackGlyph;
270
2/2
✓ Branch 14 → 5 taken 2357 times.
✓ Branch 14 → 15 taken 831 times.
3188 for (const auto &item : m_nodes) {
271 2357 const auto changed = createIndex(item->row, 0, item.get());
272
2/2
✓ Branch 9 → 10 taken 2357 times.
✓ Branch 10 → 11 taken 2357 times.
2357 emit dataChanged(changed, changed, {IconRole, ColorRole});
273 }
274 831 }
275