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: 79.0% 177 / 0 / 224
Functions: 70.6% 24 / 0 / 34
Branches: 58.5% 326 / 0 / 557

quick/CommandBarSources.cpp
Line Branch Exec Source
1 #include "CommandBarSources.h"
2
3 #include "app/core/CalendarSyncCoordinator.h"
4 #include "app/core/MailSessionCore.h"
5 #include "presentation/FolderPresentationModel.h"
6 #include "presentation/QuickCommandRouter.h"
7 #include "presentation/QuickContactModel.h"
8 #include "presentation/QuickMailBackend.h"
9 #include "presentation/QuickPimBackend.h"
10 #include "presentation/QuickTaskModel.h"
11 #include "presentation/SearchTermModel.h"
12 #include "ui/LabelDelegate.h"
13 #include "presentation/EventQuickInput.h"
14 #include "presentation/QuickRulesBackend.h"
15 #include "presentation/QuickTaskActions.h"
16 #include "presentation/QuickAccountSettings.h"
17 #include "presentation/SettingsCatalog.h"
18 #include <QSet>
19 #include <QCoreApplication>
20 #include <QHash>
21 #include <QLocale>
22
23 // Named namespace: lupdate takes it as the translation context of tr().
24 namespace CommandBarSourcesText {
25
26 244 QString tr(const char *text) { return QCoreApplication::translate("CommandBarSourcesText", text); }
27 98 QString tr(const char *text, const char *comment, int count) {
28 98 return QCoreApplication::translate("CommandBarSourcesText", text, comment, count);
29 }
30
31 89 QList<CommandCompletion::FolderEntry> folders(QuickMailBackend &backend, QuickCommandRouter &router,
32 const QString &text, bool move) {
33 89 auto *model = static_cast<FolderPresentationModel *>(backend.folders());
34 89 QStringList preferred;
35
2/2
✓ Branch 3 → 4 taken 26 times.
✓ Branch 3 → 21 taken 63 times.
89 if (move) {
36
3/3
✓ Branch 4 → 5 taken 26 times.
✓ Branch 9 → 10 taken 26 times.
✓ Branch 10 → 11 taken 26 times.
52 const QString target = backend.suggestion().value(QStringLiteral("target")).toString();
37
3/3
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 24 times.
✓ Branch 18 → 19 taken 2 times.
26 if (!target.isEmpty()) preferred << target;
38 26 }
39 89 QList<CommandCompletion::FolderEntry> result;
40 const auto candidates =
41
2/2
✓ Branch 22 → 23 taken 89 times.
✓ Branch 24 → 25 taken 89 times.
89 model->completionCandidates(text, preferred, router.history().recentFolders(backend.accountName()));
42
2/2
✓ Branch 37 → 29 taken 178 times.
✓ Branch 37 → 38 taken 89 times.
267 for (const auto &folder : candidates)
43 178 result.append({folder.path, folder.name, folder.parent, folder.unread, folder.suggested});
44 89 return result;
45 267 }
46
47 // D-19: the five standard labels under the names of the label menu, then
48 // the keywords of the selected message that are not standard labels.
49 30 QList<CommandCompletion::LabelEntry> labels(QuickMailBackend &backend) {
50 30 QList<CommandCompletion::LabelEntry> result;
51
1/1
✓ Branch 2 → 3 taken 30 times.
30 const QStringList selected = backend.selectedLabels();
52
2/2
✓ Branch 21 → 4 taken 150 times.
✓ Branch 21 → 22 taken 30 times.
180 for (int i = 1; i <= 5; ++i) {
53
1/1
✓ Branch 8 → 9 taken 150 times.
300 const QString keyword = QStringLiteral("$label%1").arg(i);
54 150 result.append({keyword, tr("Label %1").arg(i),
55 150 selected.contains(keyword, Qt::CaseInsensitive)});
56 150 }
57
2/2
✓ Branch 39 → 24 taken 6 times.
✓ Branch 39 → 40 taken 30 times.
36 for (const QString &keyword : selected)
58
2/3
✓ Branch 28 → 29 taken 6 times.
✓ Branch 31 → 32 taken 6 times.
✗ Branch 31 → 37 not taken.
12 if (!keyword.startsWith(QStringLiteral("$label"), Qt::CaseInsensitive)) result.append({keyword, keyword, true});
59 30 return result;
60 486 }
61
62 // D-21: writable task lists; a name that repeats across accounts carries
63 // its account so "/List@Account" can be inserted.
64 5 QList<CommandCompletion::TaskListEntry> taskLists(MailSessionCore &session) {
65 5 QList<CommandCompletion::TaskListEntry> result;
66 5 auto *sync = session.calendarSync();
67
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 if (!sync) return result;
68 5 QHash<QString, int> names;
69
1/1
✓ Branch 5 → 6 taken 5 times.
5 const auto calendars = sync->calendars();
70
2/2
✓ Branch 21 → 8 taken 6 times.
✓ Branch 21 → 22 taken 5 times.
11 for (const auto &calendar : calendars)
71
6/9
✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 13 not taken.
✓ Branch 14 → 15 taken 6 times.
✗ Branch 14 → 19 not taken.
✓ Branch 15 → 16 taken 6 times.
✓ Branch 16 → 17 taken 6 times.
6 if (!calendar.readOnly && calendar.supportsTasks()) ++names[calendar.displayName.toLower()];
72
2/2
✓ Branch 41 → 24 taken 6 times.
✓ Branch 41 → 42 taken 5 times.
11 for (const auto &calendar : calendars)
73
4/7
✓ Branch 25 → 26 taken 6 times.
✗ Branch 25 → 29 not taken.
✓ Branch 26 → 27 taken 6 times.
✓ Branch 27 → 28 taken 6 times.
✗ Branch 27 → 29 not taken.
✓ Branch 30 → 31 taken 6 times.
✗ Branch 30 → 39 not taken.
6 if (!calendar.readOnly && calendar.supportsTasks())
74 12 result.append({calendar.displayName, calendar.accountId, names.value(calendar.displayName.toLower()) > 1});
75 5 return result;
76 17 }
77
78 ✗ QStringList tags(QuickMailBackend &backend) {
79 ✗ QStringList result;
80 ✗ const QVariantMap counts = backend.pim()->taskModel().counts();
81 ✗ for (auto it = counts.cbegin(); it != counts.cend(); ++it)
82 ✗ if (it.key().startsWith(QStringLiteral("tag:"))) result.append(it.key().mid(4));
83 ✗ return result;
84 ✗ }
85
86 // The contact completion lane is asynchronous (latest wins): the request
87 // starts here, the rows of the last finished answer that still match are
88 // returned; the window recomputes on completionsChanged.
89 10 QList<CommandCompletion::AddressEntry> addresses(QuickMailBackend &backend, const QString &prefix) {
90 10 auto &contacts = backend.pim()->contactModel();
91 10 contacts.requestCompletions(prefix);
92 10 QList<CommandCompletion::AddressEntry> result;
93
4/4
✓ Branch 6 → 7 taken 10 times.
✓ Branch 7 → 8 taken 10 times.
✓ Branch 93 → 9 taken 1 time.
✓ Branch 93 → 94 taken 10 times.
11 for (const QVariant &value : contacts.completions()) {
94
1/1
✓ Branch 10 → 11 taken 1 time.
1 const QVariantMap entry = value.toMap();
95
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
2 const QString address = entry.value(QStringLiteral("email")).toString();
96
9/24
✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 30 not taken.
✓ Branch 27 → 28 taken 1 time.
✓ Branch 28 → 29 taken 1 time.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 34 not taken.
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 36 not taken.
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 38 not taken.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 40 not taken.
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 1 time.
✗ Branch 109 → 110 not taken.
✗ Branch 109 → 111 not taken.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 115 not taken.
✗ Branch 117 → 118 not taken.
✗ Branch 117 → 119 not taken.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 123 not taken.
2 if (address.isEmpty() || entry.value(QStringLiteral("group")).toBool()) continue;
97
2/2
✓ Branch 46 → 47 taken 1 time.
✓ Branch 47 → 48 taken 1 time.
2 const QString name = entry.value(QStringLiteral("name")).toString();
98
3/8
✓ Branch 52 → 53 taken 1 time.
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 57 taken 1 time.
✗ Branch 54 → 55 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 1 time.
1 if (!address.contains(prefix, Qt::CaseInsensitive) && !name.contains(prefix, Qt::CaseInsensitive)) continue;
99
2/3
✗ Branch 62 → 63 not taken.
✓ Branch 62 → 64 taken 1 time.
✓ Branch 69 → 70 taken 1 time.
3 result.append({address, name == address ? QString{} : name, entry.value(QStringLiteral("detail")).toString()});
100 11 }
101 10 return result;
102 3 }
103
104 // S98.1.11: the structured rows of the debounced preview lane.
105 201 QList<CommandCompletion::ResultEntry> results(QuickMailBackend &backend) {
106 201 QList<CommandCompletion::ResultEntry> result;
107 201 int index = 0;
108
4/4
✓ Branch 3 → 4 taken 201 times.
✓ Branch 4 → 5 taken 201 times.
✓ Branch 44 → 6 taken 125 times.
✓ Branch 44 → 45 taken 201 times.
326 for (const QVariant &value : backend.searchSuggestions()) {
109
1/1
✓ Branch 7 → 8 taken 125 times.
125 const QVariantMap entry = value.toMap();
110
1/1
✓ Branch 13 → 14 taken 125 times.
375 result.append({QString::number(index++), entry.value(QStringLiteral("subject")).toString(),
111
1/1
✓ Branch 19 → 20 taken 125 times.
250 entry.value(QStringLiteral("from")).toString(), QString{},
112
1/1
✓ Branch 25 → 26 taken 125 times.
250 entry.value(QStringLiteral("folderPath")).toString()});
113 326 }
114 201 return result;
115 500 }
116
117
118 349 QVariantMap chip(const QString &label, const QString &text, bool ok = true, const QString &reason = {}) {
119 1396 return {{QStringLiteral("label"), label}, {QStringLiteral("text"), text}, {QStringLiteral("ok"), ok},
120
2/4
✓ Branch 20 → 21 taken 1396 times.
✓ Branch 20 → 22 taken 349 times.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
2443 {QStringLiteral("reason"), reason}};
121 1745 }
122
123 // S98.4.12: one chip per part the parser recognised; a rejected part is a
124 // chip with ok = false and the reason. Every parser is the one that runs
125 // the line (rule 8).
126 26 QVariantList taskPreview(QuickMailBackend &backend, const QString &line) {
127
1/1
✓ Branch 4 → 5 taken 26 times.
26 const QVariantMap parts = backend.pim()->taskActions()->previewInput(line);
128 26 QVariantList chips;
129 // A token still being typed (the last word, no space after it) is not
130 // rejected yet: "@mor" becomes "@morgen" with Tab.
131
4/4
✓ Branch 7 → 8 taken 26 times.
✓ Branch 8 → 9 taken 5 times.
✓ Branch 8 → 10 taken 21 times.
✓ Branch 13 → 14 taken 21 times.
47 const QString typing = line.endsWith(QLatin1Char(' ')) ? QString{} : line.section(QLatin1Char(' '), -1);
132 45 const auto settled = [&](QChar marker, const char *okKey) {
133
11/20
✓ Branch 4 → 5 taken 45 times.
✓ Branch 5 → 6 taken 45 times.
✓ Branch 6 → 7 taken 45 times.
✓ Branch 7 → 8 taken 13 times.
✓ Branch 7 → 10 taken 32 times.
✓ Branch 8 → 9 taken 13 times.
✓ Branch 9 → 10 taken 11 times.
✓ Branch 9 → 11 taken 2 times.
✓ Branch 13 → 14 taken 45 times.
✗ Branch 13 → 15 not taken.
✓ Branch 15 → 16 taken 45 times.
✗ Branch 15 → 17 not taken.
✓ Branch 17 → 18 taken 45 times.
✗ Branch 17 → 19 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 32 not taken.
45 return parts.value(QLatin1String(okKey)).toBool() || typing.startsWith(marker);
134 26 };
135
2/2
✓ Branch 18 → 19 taken 26 times.
✓ Branch 19 → 20 taken 26 times.
52 const QVariantMap target = parts.value(QStringLiteral("target")).toMap();
136
2/2
✓ Branch 28 → 29 taken 26 times.
✓ Branch 29 → 30 taken 26 times.
52 const QString list = parts.value(QStringLiteral("list")).toString();
137
5/7
✓ Branch 35 → 36 taken 7 times.
✓ Branch 35 → 38 taken 19 times.
✓ Branch 36 → 37 taken 7 times.
✓ Branch 37 → 38 taken 7 times.
✗ Branch 37 → 39 not taken.
✓ Branch 40 → 41 taken 26 times.
✗ Branch 40 → 88 not taken.
26 if (!list.isEmpty() || !target.isEmpty())
138
8/8
✓ Branch 52 → 53 taken 26 times.
✓ Branch 53 → 54 taken 26 times.
✓ Branch 55 → 56 taken 2 times.
✓ Branch 55 → 57 taken 24 times.
✓ Branch 56 → 63 taken 2 times.
✓ Branch 62 → 63 taken 24 times.
✓ Branch 63 → 64 taken 26 times.
✓ Branch 64 → 65 taken 26 times.
102 chips << chip(tr("List"), target.value(QStringLiteral("name")).toString().isEmpty() ? list.mid(1)
139
9/17
✓ Branch 61 → 62 taken 24 times.
✓ Branch 71 → 72 taken 24 times.
✓ Branch 71 → 73 taken 2 times.
✓ Branch 73 → 74 taken 24 times.
✓ Branch 73 → 75 taken 2 times.
✓ Branch 75 → 76 taken 24 times.
✓ Branch 75 → 77 taken 2 times.
✓ Branch 77 → 78 taken 24 times.
✓ Branch 77 → 79 taken 2 times.
✗ Branch 241 → 242 not taken.
✗ Branch 241 → 243 not taken.
✗ Branch 245 → 246 not taken.
✗ Branch 245 → 247 not taken.
✗ Branch 249 → 250 not taken.
✗ Branch 249 → 251 not taken.
✗ Branch 253 → 254 not taken.
✗ Branch 253 → 255 not taken.
74 : target.value(QStringLiteral("name")).toString(),
140
5/5
✓ Branch 41 → 42 taken 26 times.
✓ Branch 43 → 44 taken 26 times.
✓ Branch 44 → 45 taken 26 times.
✓ Branch 47 → 48 taken 26 times.
✓ Branch 66 → 67 taken 26 times.
104 settled(QLatin1Char('/'), "listOk"), tr("No task list “%1”").arg(list.mid(1)));
141 // An unfinished token shows as typed, not as the parser's verdict.
142 19 const auto shown = [&](QChar marker, const char *okKey, const QString &parsed) {
143
12/21
✓ Branch 4 → 5 taken 19 times.
✓ Branch 5 → 6 taken 19 times.
✓ Branch 6 → 7 taken 19 times.
✓ Branch 7 → 8 taken 11 times.
✓ Branch 7 → 12 taken 8 times.
✓ Branch 8 → 9 taken 11 times.
✓ Branch 9 → 10 taken 10 times.
✓ Branch 9 → 12 taken 1 time.
✓ Branch 10 → 11 taken 10 times.
✓ Branch 14 → 15 taken 19 times.
✗ Branch 14 → 16 not taken.
✓ Branch 16 → 17 taken 19 times.
✗ Branch 16 → 18 not taken.
✓ Branch 18 → 19 taken 19 times.
✗ Branch 18 → 20 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
38 return !parts.value(QLatin1String(okKey)).toBool() && typing.startsWith(marker) ? typing.mid(1) : parsed;
144 26 };
145
4/4
✓ Branch 92 → 93 taken 26 times.
✓ Branch 93 → 94 taken 26 times.
✓ Branch 100 → 101 taken 14 times.
✓ Branch 100 → 129 taken 12 times.
52 if (!parts.value(QStringLiteral("due")).toString().isEmpty())
146
5/5
✓ Branch 109 → 110 taken 14 times.
✓ Branch 110 → 111 taken 14 times.
✓ Branch 113 → 114 taken 14 times.
✓ Branch 114 → 115 taken 14 times.
✓ Branch 115 → 116 taken 14 times.
42 chips << chip(tr("Due"), shown(QLatin1Char('@'), "dueOk", parts.value(QStringLiteral("due")).toString()),
147
2/2
✓ Branch 104 → 105 taken 14 times.
✓ Branch 117 → 118 taken 14 times.
42 settled(QLatin1Char('@'), "dueOk"));
148
4/4
✓ Branch 133 → 134 taken 26 times.
✓ Branch 134 → 135 taken 26 times.
✓ Branch 141 → 142 taken 5 times.
✓ Branch 141 → 170 taken 21 times.
52 if (!parts.value(QStringLiteral("priority")).toString().isEmpty())
149
5/5
✓ Branch 150 → 151 taken 5 times.
✓ Branch 151 → 152 taken 5 times.
✓ Branch 154 → 155 taken 5 times.
✓ Branch 155 → 156 taken 5 times.
✓ Branch 156 → 157 taken 5 times.
15 chips << chip(tr("Priority"), shown(QLatin1Char('!'), "priorityOk", parts.value(QStringLiteral("priorityText")).toString()),
150
2/2
✓ Branch 145 → 146 taken 5 times.
✓ Branch 158 → 159 taken 5 times.
15 settled(QLatin1Char('!'), "priorityOk"));
151
5/8
✓ Branch 174 → 175 taken 26 times.
✓ Branch 175 → 176 taken 26 times.
✓ Branch 180 → 181 taken 26 times.
✓ Branch 181 → 182 taken 26 times.
✗ Branch 186 → 187 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 195 → 183 not taken.
✓ Branch 195 → 196 taken 26 times.
52 for (const QString &tag : parts.value(QStringLiteral("tags")).toStringList()) chips << chip(QString{}, tag);
152 26 return chips;
153 26 }
154
155 5 QVariantList eventPreview(QuickMailBackend &backend, const QString &line) {
156
1/1
✓ Branch 3 → 4 taken 5 times.
5 const QVariantMap parsed = backend.eventInput()->parse(line);
157 5 QVariantList chips;
158
2/11
✓ Branch 7 → 8 taken 5 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 34 taken 5 times.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 25 not taken.
✗ Branch 202 → 203 not taken.
✗ Branch 202 → 204 not taken.
5 if (parsed.contains(QStringLiteral("error"))) return {chip(tr("Calendar"), parsed.value(QStringLiteral("error")).toString(), false)};
159
3/4
✓ Branch 38 → 39 taken 5 times.
✓ Branch 39 → 40 taken 5 times.
✓ Branch 46 → 47 taken 5 times.
✗ Branch 46 → 68 not taken.
10 if (!parsed.value(QStringLiteral("summary")).toString().isEmpty())
160
5/5
✓ Branch 52 → 53 taken 5 times.
✓ Branch 53 → 54 taken 5 times.
✓ Branch 54 → 55 taken 5 times.
✓ Branch 55 → 56 taken 5 times.
✓ Branch 57 → 58 taken 5 times.
10 chips << chip(tr("Title"), parsed.value(QStringLiteral("summary")).toString());
161
2/3
✓ Branch 71 → 72 taken 5 times.
✓ Branch 74 → 75 taken 5 times.
✗ Branch 74 → 106 not taken.
5 if (parsed.contains(QStringLiteral("dateIso")))
162
7/7
✓ Branch 76 → 77 taken 5 times.
✓ Branch 84 → 85 taken 5 times.
✓ Branch 85 → 86 taken 5 times.
✓ Branch 86 → 87 taken 5 times.
✓ Branch 87 → 88 taken 5 times.
✓ Branch 88 → 89 taken 5 times.
✓ Branch 89 → 90 taken 5 times.
25 chips << chip(tr("Date"), QLocale().toString(QDate::fromString(parsed.value(QStringLiteral("dateIso")).toString(), Qt::ISODate),
163
1/1
✓ Branch 91 → 92 taken 5 times.
20 QStringLiteral("ddd d MMM")));
164
2/3
✓ Branch 109 → 110 taken 5 times.
✓ Branch 112 → 113 taken 5 times.
✗ Branch 112 → 162 not taken.
5 if (parsed.contains(QStringLiteral("startTime")))
165
4/4
✓ Branch 131 → 132 taken 5 times.
✓ Branch 132 → 133 taken 5 times.
✓ Branch 134 → 135 taken 5 times.
✓ Branch 135 → 136 taken 5 times.
25 chips << chip(tr("Time"), parsed.value(QStringLiteral("startTime")).toString() +
166
1/1
✓ Branch 117 → 118 taken 5 times.
10 (parsed.contains(QStringLiteral("endTime"))
167
8/27
✗ Branch 118 → 119 not taken.
✓ Branch 118 → 126 taken 5 times.
✗ Branch 123 → 124 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 125 → 127 not taken.
✓ Branch 133 → 134 taken 5 times.
✓ Branch 137 → 138 taken 5 times.
✗ Branch 148 → 149 not taken.
✓ Branch 148 → 150 taken 5 times.
✗ Branch 150 → 151 not taken.
✓ Branch 150 → 152 taken 5 times.
✗ Branch 152 → 153 not taken.
✓ Branch 152 → 154 taken 5 times.
✗ Branch 154 → 155 not taken.
✓ Branch 154 → 156 taken 5 times.
✗ Branch 156 → 157 not taken.
✓ Branch 156 → 158 taken 5 times.
✗ Branch 352 → 353 not taken.
✗ Branch 352 → 354 not taken.
✗ Branch 356 → 357 not taken.
✗ Branch 356 → 358 not taken.
✗ Branch 360 → 361 not taken.
✗ Branch 360 → 362 not taken.
✗ Branch 364 → 365 not taken.
✗ Branch 364 → 366 not taken.
✗ Branch 368 → 369 not taken.
✗ Branch 368 → 370 not taken.
25 ? "–" + parsed.value(QStringLiteral("endTime")).toString() : QString{}));
168
2/8
✓ Branch 165 → 166 taken 5 times.
✗ Branch 168 → 169 not taken.
✓ Branch 168 → 190 taken 5 times.
✗ Branch 174 → 175 not taken.
✗ Branch 175 → 176 not taken.
✗ Branch 176 → 177 not taken.
✗ Branch 177 → 178 not taken.
✗ Branch 179 → 180 not taken.
5 if (parsed.contains(QStringLiteral("location"))) chips << chip(tr("Place"), parsed.value(QStringLiteral("location")).toString());
169 5 return chips;
170 5 }
171
172 // S101.6.5 (D-24, CB-04): the search tab's chips, word for word.
173 201 QVariantList searchPreview(const QString &line) {
174 201 SearchTermModel::Lookups lookups;
175 201 lookups.label = [](const QString &keyword) { return LabelDelegate::displayName(keyword); };
176 201 QVariantList chips;
177
8/8
✓ Branch 4 → 5 taken 201 times.
✓ Branch 5 → 6 taken 201 times.
✓ Branch 7 → 8 taken 201 times.
✓ Branch 8 → 9 taken 201 times.
✓ Branch 13 → 14 taken 243 times.
✓ Branch 15 → 16 taken 243 times.
✓ Branch 22 → 10 taken 243 times.
✓ Branch 22 → 23 taken 201 times.
444 for (const auto &term : SearchTermModel::terms(line, lookups)) chips << chip(QString{}, term.label);
178 201 return chips;
179 201 }
180
181 16 QVariantList rulePreview(QuickMailBackend &backend, const QString &line) {
182
8/33
✓ Branch 5 → 6 taken 16 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 17 taken 16 times.
✗ Branch 10 → 11 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 16 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 16 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 16 times.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 16 times.
✓ Branch 27 → 28 taken 16 times.
✗ Branch 27 → 29 not taken.
✓ Branch 29 → 30 taken 16 times.
✗ Branch 29 → 31 not taken.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 109 not taken.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
✗ Branch 115 → 116 not taken.
✗ Branch 115 → 117 not taken.
✗ Branch 119 → 120 not taken.
✗ Branch 119 → 121 not taken.
✗ Branch 123 → 124 not taken.
✗ Branch 123 → 125 not taken.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 129 not taken.
32 const bool arrow = line.contains(QStringLiteral("→")) || line.contains(QStringLiteral("->")) || line.contains(QStringLiteral("=>"));
183
5/6
✓ Branch 31 → 32 taken 16 times.
✗ Branch 31 → 35 not taken.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 34 → 36 taken 15 times.
✓ Branch 37 → 38 taken 1 time.
✓ Branch 37 → 39 taken 15 times.
16 if (!arrow || !backend.rules()->available()) return {};
184
1/1
✓ Branch 40 → 41 taken 15 times.
15 const QVariantMap parsed = backend.rules()->parseConsoleRule(line);
185
9/11
✓ Branch 45 → 46 taken 15 times.
✓ Branch 46 → 47 taken 15 times.
✓ Branch 51 → 52 taken 7 times.
✓ Branch 51 → 75 taken 8 times.
✓ Branch 57 → 58 taken 7 times.
✓ Branch 58 → 59 taken 7 times.
✓ Branch 60 → 61 taken 7 times.
✓ Branch 64 → 65 taken 7 times.
✓ Branch 64 → 66 taken 7 times.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
58 if (!parsed.value(QStringLiteral("ok")).toBool()) return {chip(QString{}, parsed.value(QStringLiteral("error")).toString(), false)};
186 8 QVariantList chips;
187
8/8
✓ Branch 79 → 80 taken 8 times.
✓ Branch 80 → 81 taken 8 times.
✓ Branch 85 → 86 taken 8 times.
✓ Branch 86 → 87 taken 8 times.
✓ Branch 91 → 92 taken 39 times.
✓ Branch 93 → 94 taken 39 times.
✓ Branch 100 → 88 taken 39 times.
✓ Branch 100 → 101 taken 8 times.
55 for (const QString &part : parsed.value(QStringLiteral("preview")).toStringList()) chips << chip(QString{}, part);
188 8 return chips;
189 29 }
190
191 // S98.5.8: descriptions that name what a command acts on — the selected
192 // messages and the current folder, both already held by the backend (no
193 // extra query). "" keeps the catalog's description.
194 2262 QString contextDetail(QuickMailBackend &backend, const QString &line) {
195
1/1
✓ Branch 5 → 6 taken 2262 times.
2262 const QString name = line.section(QLatin1Char(' '), 0, 0);
196
1/1
✓ Branch 9 → 10 taken 2262 times.
2262 const QString value = line.section(QLatin1Char(' '), 1, 1);
197
1/1
✓ Branch 13 → 14 taken 2262 times.
2262 const QString rest = line.section(QLatin1Char(' '), 1);
198
1/1
✓ Branch 14 → 15 taken 2262 times.
2262 const int count = int(backend.selectionCount());
199 2262 const QString folder = backend.folderPath();
200 // One message keeps the catalog text; with several selected the mail
201 // actions act on all of them (MAIL-SELECTION-ACTIONS) and say how many.
202
2/2
✓ Branch 16 → 17 taken 166 times.
✓ Branch 16 → 109 taken 2096 times.
2262 if (count > 1) {
203
3/3
✓ Branch 19 → 20 taken 24 times.
✓ Branch 19 → 22 taken 142 times.
✓ Branch 20 → 21 taken 24 times.
166 if (name == QLatin1String("archive")) return tr("Archive %n messages", nullptr, count);
204
3/3
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 27 taken 140 times.
✓ Branch 25 → 26 taken 2 times.
142 if (name == QLatin1String("delete")) return tr("Move %n messages to the trash", nullptr, count);
205
1/3
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 32 taken 140 times.
✗ Branch 30 → 31 not taken.
140 if (name == QLatin1String("junk")) return tr("Mark %n messages as junk", nullptr, count);
206
4/6
✓ Branch 34 → 35 taken 17 times.
✓ Branch 34 → 38 taken 123 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 17 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 45 taken 140 times.
140 if (name == QLatin1String("move") && !rest.isEmpty())
207 ✗ return tr("Move %n messages to %1", nullptr, count).arg(rest);
208
3/3
✓ Branch 47 → 48 taken 17 times.
✓ Branch 47 → 50 taken 123 times.
✓ Branch 48 → 49 taken 17 times.
140 if (name == QLatin1String("move")) return tr("Move %n messages to a folder", nullptr, count);
209
7/7
✓ Branch 52 → 53 taken 30 times.
✓ Branch 52 → 57 taken 93 times.
✓ Branch 55 → 56 taken 1 time.
✓ Branch 55 → 57 taken 29 times.
✓ Branch 58 → 59 taken 1 time.
✓ Branch 58 → 61 taken 122 times.
✓ Branch 59 → 60 taken 1 time.
123 if (name == QLatin1String("mark") && value == QLatin1String("read")) return tr("Mark %n messages as read", nullptr, count);
210
7/7
✓ Branch 63 → 64 taken 29 times.
✓ Branch 63 → 68 taken 93 times.
✓ Branch 66 → 67 taken 1 time.
✓ Branch 66 → 68 taken 28 times.
✓ Branch 69 → 70 taken 1 time.
✓ Branch 69 → 72 taken 121 times.
✓ Branch 70 → 71 taken 1 time.
122 if (name == QLatin1String("mark") && value == QLatin1String("unread")) return tr("Mark %n messages as unread", nullptr, count);
211
6/6
✓ Branch 74 → 75 taken 28 times.
✓ Branch 74 → 79 taken 93 times.
✓ Branch 77 → 78 taken 9 times.
✓ Branch 77 → 79 taken 19 times.
✓ Branch 80 → 81 taken 9 times.
✓ Branch 80 → 83 taken 112 times.
121 if (name == QLatin1String("mark") && value == QLatin1String("important"))
212
1/1
✓ Branch 81 → 82 taken 9 times.
9 return tr("Mark %n messages as important", nullptr, count);
213
6/6
✓ Branch 85 → 86 taken 19 times.
✓ Branch 85 → 90 taken 93 times.
✓ Branch 88 → 89 taken 9 times.
✓ Branch 88 → 90 taken 10 times.
✓ Branch 91 → 92 taken 9 times.
✓ Branch 91 → 94 taken 103 times.
112 if (name == QLatin1String("mark") && value == QLatin1String("unimportant"))
214
1/1
✓ Branch 92 → 93 taken 9 times.
9 return tr("Remove the important mark from %n messages", nullptr, count);
215
3/3
✓ Branch 96 → 97 taken 10 times.
✓ Branch 96 → 99 taken 93 times.
✓ Branch 97 → 98 taken 10 times.
103 if (name == QLatin1String("mark")) return tr("Mark %n messages", nullptr, count);
216
3/3
✓ Branch 101 → 102 taken 20 times.
✓ Branch 101 → 104 taken 73 times.
✓ Branch 102 → 103 taken 20 times.
93 if (name == QLatin1String("label")) return tr("Label %n messages", nullptr, count);
217
3/3
✓ Branch 106 → 107 taken 5 times.
✓ Branch 106 → 109 taken 68 times.
✓ Branch 107 → 108 taken 5 times.
73 if (name == QLatin1String("unlabel")) return tr("Remove a label from %n messages", nullptr, count);
218 }
219
5/6
✓ Branch 111 → 112 taken 12 times.
✓ Branch 111 → 115 taken 2152 times.
✓ Branch 113 → 114 taken 12 times.
✗ Branch 113 → 115 not taken.
✓ Branch 116 → 117 taken 12 times.
✓ Branch 116 → 157 taken 2152 times.
2164 if (name == QLatin1String("folder") && !folder.isEmpty()) {
220
1/4
✗ Branch 119 → 120 not taken.
✓ Branch 119 → 125 taken 12 times.
✗ Branch 120 → 121 not taken.
✗ Branch 122 → 123 not taken.
12 if (value == QLatin1String("rename")) return tr("Rename “%1”").arg(folder);
221
4/4
✓ Branch 127 → 128 taken 3 times.
✓ Branch 127 → 133 taken 9 times.
✓ Branch 128 → 129 taken 3 times.
✓ Branch 130 → 131 taken 3 times.
15 if (value == QLatin1String("delete")) return tr("Delete “%1” (asks first)").arg(folder);
222
4/4
✓ Branch 135 → 136 taken 4 times.
✓ Branch 135 → 141 taken 5 times.
✓ Branch 136 → 137 taken 4 times.
✓ Branch 138 → 139 taken 4 times.
13 if (value == QLatin1String("move")) return tr("Move “%1” under another folder").arg(folder);
223
1/4
✗ Branch 143 → 144 not taken.
✓ Branch 143 → 149 taken 5 times.
✗ Branch 144 → 145 not taken.
✗ Branch 146 → 147 not taken.
5 if (value == QLatin1String("new")) return tr("New folder inside “%1”").arg(folder);
224
4/4
✓ Branch 151 → 152 taken 1 time.
✓ Branch 151 → 157 taken 4 times.
✓ Branch 152 → 153 taken 1 time.
✓ Branch 154 → 155 taken 1 time.
6 if (value == QLatin1String("properties")) return tr("Properties of “%1”").arg(folder);
225 }
226 2156 return {};
227 2262 }
228
229 // Sprint 100 D-29: settings places — every page when nothing is typed,
230 // else places whose id starts with the text, the accounts
231 // ("accounts/<address>") and the catalog search (D-8), at most 12.
232 ✗ QList<CommandCompletion::PlaceEntry> places(QuickMailBackend &backend, const QString &needle) {
233 ✗ QList<CommandCompletion::PlaceEntry> result;
234 ✗ QSet<QString> seen;
235 ✗ SettingsCatalog catalog;
236 ✗ const auto add = [&](const QString &place, const QString &label, const QString &path) {
237 ✗ if (place.isEmpty() || seen.contains(place) || result.size() >= 12) return;
238 ✗ seen.insert(place);
239 ✗ result.append({place, label, path});
240 ✗ };
241 ✗ const auto addPlace = [&](const QString &id) {
242 ✗ const QVariantMap place = catalog.place(id);
243 ✗ add(id, place.value(QStringLiteral("label")).toString(), place.value(QStringLiteral("path")).toString());
244 ✗ };
245 ✗ if (needle.isEmpty()) {
246 ✗ for (const auto &page : SettingsCatalog::pages()) addPlace(QString::fromLatin1(page.id));
247 ✗ return result;
248 }
249 ✗ const auto starts = [&needle](const QString &id) { return id.startsWith(needle, Qt::CaseInsensitive); };
250 ✗ for (const auto &page : SettingsCatalog::pages())
251 ✗ if (starts(QString::fromLatin1(page.id))) addPlace(QString::fromLatin1(page.id));
252 ✗ for (const auto &setting : SettingsCatalog::settings())
253 ✗ if (starts(QString::fromLatin1(setting.id))) addPlace(QString::fromLatin1(setting.id));
254 ✗ for (const auto &entry : backend.settings()->configuredAccounts()) {
255 ✗ const QString email = entry.toMap().value(QStringLiteral("email")).toString();
256 ✗ const QString place = QStringLiteral("accounts/") + email;
257 ✗ if (starts(place) || email.contains(needle, Qt::CaseInsensitive)) add(place, email, catalog.label(QStringLiteral("accounts")));
258 ✗ }
259 ✗ for (const auto &hit : SettingsCatalog::search(needle)) add(hit.place, hit.label, hit.path);
260 ✗ return result;
261 ✗ }
262
263 ✗ QList<CommandCompletion::AccountEntry> accounts(QuickMailBackend &backend) {
264 ✗ QList<CommandCompletion::AccountEntry> result;
265 ✗ const QString active = backend.activeAccountId();
266 ✗ for (const auto &entry : backend.settings()->configuredAccounts()) {
267 ✗ const auto account = entry.toMap();
268 ✗ result.append({account.value(QStringLiteral("email")).toString(), account.value(QStringLiteral("name")).toString(),
269 ✗ account.value(QStringLiteral("id")).toString() == active});
270 ✗ }
271 ✗ return result;
272 ✗ }
273
274 } // namespace CommandBarSourcesText
275
276 using namespace CommandBarSourcesText;
277
278 765 CommandCompletion::Sources commandBarSources(MailSessionCore &session, QuickMailBackend &backend,
279 QuickCommandRouter &router) {
280 765 CommandCompletion::Sources sources;
281 3782 sources.unavailableReason = [&backend](const QString &line) { return backend.namedCommandUnavailableReason(line); };
282 854 sources.folders = [&backend, &router](const QString &text, bool move) { return folders(backend, router, text, move); };
283 795 sources.labels = [&backend] { return labels(backend); };
284 770 sources.taskLists = [&session] { return taskLists(session); };
285 765 sources.tags = [&backend] { return tags(backend); };
286 775 sources.addresses = [&backend](const QString &prefix) { return addresses(backend, prefix); };
287 966 sources.results = [&backend](const QString &) { return results(backend); };
288 3027 sources.contextDetail = [&backend](const QString &line) { return contextDetail(backend, line); };
289 765 sources.places = [&backend](const QString &needle) { return places(backend, needle); };
290 765 sources.accounts = [&backend] { return accounts(backend); };
291 1995 sources.linePreview = [&backend](const QString &command, const QString &untrimmed) {
292
3/3
✓ Branch 4 → 5 taken 26 times.
✓ Branch 4 → 7 taken 439 times.
✓ Branch 5 → 6 taken 26 times.
465 if (command == QLatin1String("task")) return taskPreview(backend, untrimmed);
293
1/1
✓ Branch 7 → 8 taken 439 times.
439 const QString line = untrimmed.trimmed();
294
3/3
✓ Branch 10 → 11 taken 5 times.
✓ Branch 10 → 13 taken 434 times.
✓ Branch 11 → 12 taken 5 times.
439 if (command == QLatin1String("event")) return eventPreview(backend, line);
295
8/8
✓ Branch 15 → 16 taken 203 times.
✓ Branch 15 → 20 taken 231 times.
✓ Branch 17 → 18 taken 203 times.
✓ Branch 18 → 19 taken 201 times.
✓ Branch 18 → 20 taken 2 times.
✓ Branch 21 → 22 taken 201 times.
✓ Branch 21 → 24 taken 233 times.
✓ Branch 22 → 23 taken 201 times.
434 if (command == QLatin1String("search") && !line.startsWith(QLatin1String("in:"), Qt::CaseInsensitive)) return searchPreview(line);
296
3/3
✓ Branch 26 → 27 taken 16 times.
✓ Branch 26 → 29 taken 217 times.
✓ Branch 27 → 28 taken 16 times.
233 if (command == QLatin1String("rule")) return rulePreview(backend, line);
297 217 return QVariantList{};
298 1204 };
299 765 return sources;
300 }
301