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.5% 285 / 0 / 308
Functions: 97.8% 45 / 0 / 46
Branches: 80.4% 361 / 0 / 449

presentation/SearchTermModel.cpp
Line Branch Exec Source
1 #include "SearchTermModel.h"
2
3 #include "app/SearchQuery.h"
4 #include "app/SearchVocabulary.h"
5 #include "app/TimeHistogram.h"
6 #include <QCoreApplication>
7 #include <utility>
8
9 namespace SearchTermModel {
10
11 namespace {
12
13 using Tri = MailCache::SearchFilter::Tri;
14
15 struct Parsed {
16 QString text;
17 MailCache::SearchFilter filter;
18 };
19
20 7435 Parsed parse(const QString &query) {
21 7435 Parsed parsed;
22
1/1
✓ Branch 3 → 4 taken 7435 times.
7435 parsed.text = parseSearchQuery(query, parsed.filter);
23 7435 return parsed;
24 ✗ }
25
26 679 QString build(const Parsed &parsed) { return buildSearchQuery(parsed.text, parsed.filter); }
27
28 5809 QString tr(const char *text) { return QCoreApplication::translate("SearchTermModel", text); }
29
30
2/2
✓ Branch 2 → 3 taken 3288 times.
✓ Branch 4 → 5 taken 3288 times.
6576 QString quoted(const QString &value) { return tr("“%1”").arg(value); }
31
32 // A person's name for an address-like value, else the value.
33 2634 QString person(const QString &value, const Lookups &lookups) {
34
6/7
✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 9 taken 2614 times.
✓ Branch 6 → 7 taken 20 times.
✓ Branch 7 → 8 taken 20 times.
✗ Branch 7 → 9 not taken.
✓ Branch 10 → 11 taken 20 times.
✓ Branch 10 → 19 taken 2614 times.
2634 if (lookups.name && value.contains(QLatin1Char('@'))) {
35 20 const QString name = lookups.name(value);
36
2/2
✓ Branch 13 → 14 taken 14 times.
✓ Branch 13 → 15 taken 6 times.
20 if (!name.isEmpty()) return name;
37 6 }
38 2620 return value;
39 }
40
41 26 QString labelName(const QString &keyword, const Lookups &lookups) {
42
3/3
✓ Branch 3 → 4 taken 20 times.
✓ Branch 3 → 5 taken 6 times.
✓ Branch 4 → 6 taken 20 times.
26 const QString name = lookups.label ? lookups.label(keyword) : QString();
43
2/2
✓ Branch 7 → 8 taken 6 times.
✓ Branch 7 → 9 taken 20 times.
52 return name.isEmpty() ? keyword : name;
44 26 }
45
46 2 QString presetLabel(const QString &preset) {
47
4/5
✓ Branch 5 → 6 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 19 → 11 taken 8 times.
✗ Branch 19 → 20 not taken.
8 for (const auto &word : SearchVocabulary::valuesFor(QStringLiteral("date:")))
48
4/5
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 17 taken 6 times.
✓ Branch 15 → 16 taken 2 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 25 taken 2 times.
8 if (preset == QLatin1String(word.word)) return SearchVocabulary::description(word);
49 ✗ return preset;
50 }
51
52 240 QString dateLabel(const MailCache::SearchFilter &filter, const QLocale &locale) {
53
3/3
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 238 times.
✓ Branch 4 → 5 taken 2 times.
240 if (!filter.datePreset.isEmpty()) return presetLabel(filter.datePreset);
54
1/1
✓ Branch 6 → 7 taken 238 times.
238 const QDate from = filter.dateFrom.date();
55
1/1
✓ Branch 7 → 8 taken 238 times.
238 const QDate to = filter.dateTo.date();
56
8/9
✓ Branch 8 → 9 taken 238 times.
✓ Branch 9 → 10 taken 238 times.
✗ Branch 9 → 13 not taken.
✓ Branch 10 → 11 taken 238 times.
✓ Branch 11 → 12 taken 237 times.
✓ Branch 11 → 13 taken 1 time.
✓ Branch 14 → 15 taken 237 times.
✓ Branch 14 → 17 taken 1 time.
✓ Branch 15 → 16 taken 237 times.
238 if (from.isValid() && to.isValid()) return TimeHistogram::rangeLabel({from, to}, locale);
57
5/6
✓ Branch 17 → 18 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 27 not taken.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
2 if (from.isValid()) return tr("Since %1").arg(locale.toString(from, QLocale::ShortFormat));
58 ✗ return tr("Until %1").arg(locale.toString(to, QLocale::ShortFormat));
59 }
60
61 3616 Term make(Kind kind, const QString &value, const QString &label, const QString &shortLabel, bool negated = false) {
62 3616 Term term;
63 3616 term.kind = kind;
64 3616 term.value = value;
65 3616 term.label = label;
66 3616 term.shortLabel = shortLabel;
67 3616 term.negated = negated;
68 3616 return term;
69 }
70
71 10504 void appendState(QList<Term> &terms, Kind kind, Tri value, const char *yes, const char *no) {
72
2/2
✓ Branch 2 → 3 taken 10301 times.
✓ Branch 2 → 4 taken 203 times.
10504 if (value == Tri::Any) return;
73
3/3
✓ Branch 4 → 5 taken 199 times.
✓ Branch 4 → 6 taken 4 times.
✓ Branch 7 → 8 taken 203 times.
203 const QString label = tr(value == Tri::Yes ? yes : no);
74
7/11
✓ Branch 8 → 9 taken 199 times.
✓ Branch 8 → 12 taken 4 times.
✓ Branch 16 → 17 taken 203 times.
✓ Branch 19 → 20 taken 4 times.
✓ Branch 19 → 21 taken 199 times.
✓ Branch 21 → 22 taken 199 times.
✓ Branch 21 → 23 taken 4 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 37 not taken.
406 terms.append(make(kind, value == Tri::Yes ? QStringLiteral("yes") : QStringLiteral("no"), label, label,
75 value == Tri::No));
76 203 }
77
78 15756 void appendExcluded(QList<Term> &terms, Kind kind, const QStringList &values, const char *pattern,
79 const std::function<QString(const QString &)> &shown) {
80
2/2
✓ Branch 19 → 4 taken 96 times.
✓ Branch 19 → 20 taken 15756 times.
15852 for (const QString &value : values)
81
5/5
✓ Branch 5 → 6 taken 96 times.
✓ Branch 6 → 7 taken 96 times.
✓ Branch 8 → 9 taken 96 times.
✓ Branch 9 → 10 taken 96 times.
✓ Branch 11 → 12 taken 96 times.
192 terms.append(make(kind, value, tr(pattern).arg(shown(value)), shown(value), true));
82 15756 }
83
84 // The positive value constraints in chip order.
85 2626 void appendValues(QList<Term> &terms, const MailCache::SearchFilter &f, const Lookups &lookups) {
86
2/2
✓ Branch 3 → 4 taken 1304 times.
✓ Branch 3 → 17 taken 1322 times.
2626 if (!f.fromFilter.isEmpty())
87
5/5
✓ Branch 4 → 5 taken 1304 times.
✓ Branch 5 → 6 taken 1304 times.
✓ Branch 7 → 8 taken 1304 times.
✓ Branch 8 → 9 taken 1304 times.
✓ Branch 10 → 11 taken 1304 times.
2608 terms.append(make(Kind::From, f.fromFilter, tr("From %1").arg(person(f.fromFilter, lookups)), person(f.fromFilter, lookups)));
88
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 32 taken 2626 times.
2626 if (!f.toFilter.isEmpty())
89 ✗ terms.append(make(Kind::To, f.toFilter, tr("To %1").arg(person(f.toFilter, lookups)), person(f.toFilter, lookups)));
90
2/2
✓ Branch 45 → 34 taken 114 times.
✓ Branch 45 → 46 taken 2626 times.
2740 for (const QString &folder : f.folderPatterns)
91
3/3
✓ Branch 35 → 36 taken 114 times.
✓ Branch 37 → 38 taken 114 times.
✓ Branch 39 → 40 taken 114 times.
228 terms.append(make(Kind::Folder, folder, tr("In %1").arg(folder), folder));
92
2/2
✓ Branch 47 → 48 taken 55 times.
✓ Branch 47 → 61 taken 2571 times.
2626 if (!f.subjectFilter.isEmpty())
93
5/5
✓ Branch 48 → 49 taken 55 times.
✓ Branch 49 → 50 taken 55 times.
✓ Branch 51 → 52 taken 55 times.
✓ Branch 52 → 53 taken 55 times.
✓ Branch 54 → 55 taken 55 times.
110 terms.append(make(Kind::Subject, f.subjectFilter, tr("Subject %1").arg(quoted(f.subjectFilter)), quoted(f.subjectFilter)));
94
2/2
✓ Branch 62 → 63 taken 11 times.
✓ Branch 62 → 72 taken 2615 times.
2626 if (!f.listFilter.isEmpty())
95
3/3
✓ Branch 63 → 64 taken 11 times.
✓ Branch 65 → 66 taken 11 times.
✓ Branch 67 → 68 taken 11 times.
22 terms.append(make(Kind::List, f.listFilter, tr("List %1").arg(f.listFilter), f.listFilter));
96
2/2
✓ Branch 89 → 74 taken 13 times.
✓ Branch 89 → 90 taken 2626 times.
2639 for (const QString &tag : f.tags)
97
5/5
✓ Branch 75 → 76 taken 13 times.
✓ Branch 76 → 77 taken 13 times.
✓ Branch 78 → 79 taken 13 times.
✓ Branch 79 → 80 taken 13 times.
✓ Branch 81 → 82 taken 13 times.
26 terms.append(make(Kind::Label, tag, tr("Label %1").arg(labelName(tag, lookups)), labelName(tag, lookups)));
98
1/2
✗ Branch 91 → 92 not taken.
✓ Branch 91 → 101 taken 2626 times.
2626 if (!f.aboutFilter.isEmpty())
99 ✗ terms.append(make(Kind::About, f.aboutFilter, tr("About %1").arg(f.aboutFilter), f.aboutFilter));
100 2626 }
101
102 2626 void appendExclusions(QList<Term> &terms, const MailCache::SearchFilter &f, const Lookups &lookups) {
103 166 const auto same = [](const QString &value) { return value; };
104 26 const auto name = [&lookups](const QString &value) { return person(value, lookups); };
105
1/1
✓ Branch 3 → 4 taken 2626 times.
2626 appendExcluded(terms, Kind::Folder, f.excludedFolders, QT_TRANSLATE_NOOP("SearchTermModel", "Not in %1"), same);
106
1/1
✓ Branch 6 → 7 taken 2626 times.
2626 appendExcluded(terms, Kind::From, f.excludedFrom, QT_TRANSLATE_NOOP("SearchTermModel", "Not from %1"), name);
107
1/1
✓ Branch 9 → 10 taken 2626 times.
2626 appendExcluded(terms, Kind::To, f.excludedTo, QT_TRANSLATE_NOOP("SearchTermModel", "Not to %1"), name);
108
1/1
✓ Branch 12 → 13 taken 2626 times.
2626 appendExcluded(terms, Kind::List, f.excludedLists, QT_TRANSLATE_NOOP("SearchTermModel", "Not on list %1"), same);
109
1/1
✓ Branch 15 → 16 taken 2626 times.
2626 appendExcluded(terms, Kind::Subject, f.excludedSubjects, QT_TRANSLATE_NOOP("SearchTermModel", "Subject without %1"), quoted);
110
1/1
✓ Branch 18 → 19 taken 2626 times.
2626 appendExcluded(terms, Kind::Label, f.excludedTags, QT_TRANSLATE_NOOP("SearchTermModel", "Without label %1"),
111 2626 [&lookups](const QString &value) { return labelName(value, lookups); });
112 2626 }
113
114 // The value list a term of `kind` lives in (positive or negated); nullptr
115 // for single-valued positive kinds.
116 55 QStringList *listFor(MailCache::SearchFilter &f, Kind kind, bool negated) {
117
5/8
✓ Branch 2 → 3 taken 15 times.
✓ Branch 2 → 7 taken 1 time.
✓ Branch 2 → 11 taken 37 times.
✓ Branch 2 → 15 taken 1 time.
✗ Branch 2 → 19 not taken.
✗ Branch 2 → 23 not taken.
✓ Branch 2 → 27 taken 1 time.
✗ Branch 2 → 28 not taken.
55 switch (kind) {
118
2/2
✓ Branch 3 → 4 taken 7 times.
✓ Branch 3 → 5 taken 8 times.
15 case Kind::Folder: return negated ? &f.excludedFolders : &f.folderPatterns;
119
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 case Kind::Label: return negated ? &f.excludedTags : &f.tags;
120
2/2
✓ Branch 11 → 12 taken 6 times.
✓ Branch 11 → 13 taken 31 times.
37 case Kind::From: return negated ? &f.excludedFrom : nullptr;
121
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 1 time.
1 case Kind::To: return negated ? &f.excludedTo : nullptr;
122 ✗ case Kind::Subject: return negated ? &f.excludedSubjects : nullptr;
123 ✗ case Kind::List: return negated ? &f.excludedLists : nullptr;
124 1 case Kind::ExcludedText: return &f.excludedTerms;
125 ✗ default: return nullptr;
126 }
127 }
128
129 32 QString *singleFor(MailCache::SearchFilter &f, Kind kind) {
130
2/6
✓ Branch 2 → 3 taken 31 times.
✓ Branch 2 → 4 taken 1 time.
✗ Branch 2 → 5 not taken.
✗ Branch 2 → 6 not taken.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
32 switch (kind) {
131 31 case Kind::From: return &f.fromFilter;
132 1 case Kind::To: return &f.toFilter;
133 ✗ case Kind::Subject: return &f.subjectFilter;
134 ✗ case Kind::List: return &f.listFilter;
135 ✗ case Kind::About: return &f.aboutFilter;
136 ✗ default: return nullptr;
137 }
138 }
139
140 597 Tri *stateFor(MailCache::SearchFilter &f, Kind kind) {
141
5/5
✓ Branch 2 → 3 taken 194 times.
✓ Branch 2 → 4 taken 174 times.
✓ Branch 2 → 5 taken 9 times.
✓ Branch 2 → 6 taken 187 times.
✓ Branch 2 → 7 taken 33 times.
597 switch (kind) {
142 194 case Kind::Unread: return &f.unread;
143 174 case Kind::Flagged: return &f.flagged;
144 9 case Kind::Answered: return &f.answered;
145 187 case Kind::Attachment: return &f.hasAttachment;
146 33 default: return nullptr;
147 }
148 }
149
150 // Removes `term` from parsed; returns false when the term is not there.
151 32 bool erase(Parsed &parsed, const Term &term) {
152 32 auto &f = parsed.filter;
153
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 30 times.
32 if (term.kind == Kind::Text) { parsed.text.clear(); return true; }
154
2/2
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 14 taken 28 times.
30 if (term.kind == Kind::Date) {
155 2 f.dateFrom = {}; f.dateTo = {}; f.datePreset.clear();
156 2 return true;
157 }
158
2/2
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 17 taken 26 times.
28 if (Tri *state = stateFor(f, term.kind)) { *state = Tri::Any; return true; }
159
2/2
✓ Branch 18 → 19 taken 8 times.
✓ Branch 18 → 21 taken 18 times.
26 if (QStringList *list = listFor(f, term.kind, term.negated)) return list->removeOne(term.value);
160
1/2
✓ Branch 22 → 23 taken 18 times.
✗ Branch 22 → 25 not taken.
18 if (QString *single = singleFor(f, term.kind)) { single->clear(); return true; }
161 ✗ return false;
162 }
163
164 // Adds a value constraint to parsed (a single-valued positive kind is replaced).
165 30 void insert(Parsed &parsed, Kind kind, const QString &value, bool negated) {
166 30 auto &f = parsed.filter;
167
3/3
✓ Branch 2 → 3 taken 30 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 29 times.
30 if (value.trimmed().isEmpty()) return;
168
2/2
✓ Branch 8 → 9 taken 15 times.
✓ Branch 8 → 12 taken 14 times.
29 if (QStringList *list = listFor(f, kind, negated)) {
169
1/2
✓ Branch 10 → 11 taken 15 times.
✗ Branch 10 → 15 not taken.
15 if (!list->contains(value)) list->append(value);
170
1/2
✓ Branch 13 → 14 taken 14 times.
✗ Branch 13 → 15 not taken.
14 } else if (QString *single = singleFor(f, kind)) {
171 14 *single = value;
172 }
173 }
174
175 } // namespace
176
177 629 QVariantMap Term::toMap() const {
178
1/1
✓ Branch 5 → 6 taken 629 times.
1887 return {{QStringLiteral("kind"), kindName(kind)}, {QStringLiteral("value"), value},
179 1887 {QStringLiteral("label"), label}, {QStringLiteral("shortLabel"), shortLabel},
180 1887 {QStringLiteral("negated"), negated}, {QStringLiteral("index"), index},
181
3/5
✓ Branch 30 → 31 taken 629 times.
✓ Branch 34 → 35 taken 4403 times.
✓ Branch 34 → 36 taken 629 times.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
6290 {QStringLiteral("relaxation"), relaxationLabel(*this)}};
182 5032 }
183
184 2626 QList<Term> terms(const QString &query, const Lookups &lookups, const QLocale &locale) {
185
1/1
✓ Branch 2 → 3 taken 2626 times.
2626 const Parsed parsed = parse(query);
186 2626 const auto &f = parsed.filter;
187 2626 QList<Term> result;
188
5/5
✓ Branch 4 → 5 taken 1577 times.
✓ Branch 4 → 13 taken 1049 times.
✓ Branch 5 → 6 taken 1577 times.
✓ Branch 6 → 7 taken 1577 times.
✓ Branch 8 → 9 taken 1577 times.
2626 if (!parsed.text.isEmpty()) result.append(make(Kind::Text, parsed.text, quoted(parsed.text), quoted(parsed.text)));
189
2/2
✓ Branch 30 → 15 taken 3 times.
✓ Branch 30 → 31 taken 2626 times.
2629 for (const QString &word : f.excludedTerms)
190
5/5
✓ Branch 16 → 17 taken 3 times.
✓ Branch 17 → 18 taken 3 times.
✓ Branch 19 → 20 taken 3 times.
✓ Branch 20 → 21 taken 3 times.
✓ Branch 22 → 23 taken 3 times.
6 result.append(make(Kind::ExcludedText, word, tr("Without %1").arg(quoted(word)), quoted(word), true));
191
1/1
✓ Branch 31 → 32 taken 2626 times.
2626 appendValues(result, f, lookups);
192
7/8
✓ Branch 32 → 33 taken 2626 times.
✓ Branch 33 → 34 taken 2386 times.
✓ Branch 33 → 36 taken 240 times.
✓ Branch 34 → 35 taken 2386 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 2386 times.
✓ Branch 38 → 39 taken 240 times.
✓ Branch 38 → 49 taken 2386 times.
2626 if (f.dateFrom.isValid() || f.dateTo.isValid()) {
193
1/1
✓ Branch 39 → 40 taken 240 times.
240 const QString label = dateLabel(f, locale);
194
4/4
✓ Branch 40 → 41 taken 240 times.
✓ Branch 41 → 42 taken 240 times.
✓ Branch 42 → 43 taken 240 times.
✓ Branch 44 → 45 taken 240 times.
240 result.append(make(Kind::Date, canonicalDateToken(f.dateFrom.date(), f.dateTo.date()), label, label));
195 240 }
196
1/1
✓ Branch 49 → 50 taken 2626 times.
2626 appendState(result, Kind::Unread, f.unread, QT_TRANSLATE_NOOP("SearchTermModel", "Unread"),
197 QT_TRANSLATE_NOOP("SearchTermModel", "Read"));
198
1/1
✓ Branch 50 → 51 taken 2626 times.
2626 appendState(result, Kind::Flagged, f.flagged, QT_TRANSLATE_NOOP("SearchTermModel", "Important"),
199 QT_TRANSLATE_NOOP("SearchTermModel", "Not important"));
200
1/1
✓ Branch 51 → 52 taken 2626 times.
2626 appendState(result, Kind::Answered, f.answered, QT_TRANSLATE_NOOP("SearchTermModel", "Answered"),
201 QT_TRANSLATE_NOOP("SearchTermModel", "Not answered"));
202
1/1
✓ Branch 52 → 53 taken 2626 times.
2626 appendState(result, Kind::Attachment, f.hasAttachment, QT_TRANSLATE_NOOP("SearchTermModel", "With attachment"),
203 QT_TRANSLATE_NOOP("SearchTermModel", "Without attachment"));
204
1/1
✓ Branch 53 → 54 taken 2626 times.
2626 appendExclusions(result, f, lookups);
205
3/3
✓ Branch 55 → 56 taken 3616 times.
✓ Branch 58 → 55 taken 3616 times.
✓ Branch 58 → 59 taken 2626 times.
6242 for (int i = 0; i < result.size(); ++i) result[i].index = i;
206 2626 return result;
207 2626 }
208
209 382 QVariantList termMaps(const QString &query, const Lookups &lookups, const QLocale &locale) {
210 382 QVariantList maps;
211
7/7
✓ Branch 2 → 3 taken 382 times.
✓ Branch 3 → 4 taken 382 times.
✓ Branch 4 → 5 taken 382 times.
✓ Branch 7 → 8 taken 629 times.
✓ Branch 9 → 10 taken 629 times.
✓ Branch 14 → 6 taken 629 times.
✓ Branch 14 → 15 taken 382 times.
1011 for (const Term &term : terms(query, lookups, locale)) maps.append(term.toMap());
212 382 return maps;
213 ✗ }
214
215
2/2
✓ Branch 2 → 3 taken 375 times.
✓ Branch 3 → 4 taken 375 times.
375 QString canonical(const QString &query) { return build(parse(query)); }
216
217
1/1
✓ Branch 2 → 3 taken 1 time.
1 QString freeText(const QString &query) { return parse(query).text; }
218
219 574 QString fieldText(const QString &query) {
220
1/1
✓ Branch 2 → 3 taken 574 times.
574 const Parsed parsed = parse(query);
221 574 MailCache::SearchFilter words;
222 574 words.excludedTerms = parsed.filter.excludedTerms;
223
1/1
✓ Branch 5 → 6 taken 574 times.
1148 return buildSearchQuery(parsed.text, words);
224 574 }
225
226 169 QString withFieldText(const QString &query, const QString &text) {
227
1/1
✓ Branch 2 → 3 taken 169 times.
169 Parsed parsed = parse(query);
228
1/1
✓ Branch 3 → 4 taken 169 times.
169 parsed.filter.excludedTerms.clear();
229 169 Parsed typed;
230 169 typed.filter = parsed.filter;
231
1/1
✓ Branch 6 → 7 taken 169 times.
169 typed.text = parseSearchQuery(text, typed.filter);
232
1/1
✓ Branch 9 → 10 taken 169 times.
338 return build(typed);
233 169 }
234
235 24 QString remove(const QString &query, int index) {
236
1/1
✓ Branch 2 → 3 taken 24 times.
48 const auto all = terms(query);
237
6/7
✓ Branch 8 → 9 taken 24 times.
✗ Branch 8 → 11 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 23 times.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 16 taken 23 times.
✓ Branch 14 → 15 taken 1 time.
24 if (index < 0 || index >= all.size()) return canonical(query);
238
1/1
✓ Branch 16 → 17 taken 23 times.
23 Parsed parsed = parse(query);
239
1/1
✓ Branch 18 → 19 taken 23 times.
23 erase(parsed, all.at(index));
240
1/1
✓ Branch 19 → 20 taken 23 times.
23 return build(parsed);
241 48 }
242
243 4 QString replace(const QString &query, int index, const QString &value) {
244
1/1
✓ Branch 2 → 3 taken 4 times.
8 const auto all = terms(query);
245
6/14
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 14 not taken.
✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 14 not taken.
✓ Branch 11 → 12 taken 4 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 4 times.
✓ Branch 16 → 17 taken 4 times.
✗ Branch 16 → 18 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 21 taken 4 times.
✗ Branch 19 → 20 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 53 not taken.
4 if (index < 0 || index >= all.size() || value.trimmed().isEmpty()) return canonical(query);
246 4 const Term &term = all.at(index);
247
1/1
✓ Branch 22 → 23 taken 4 times.
4 Parsed parsed = parse(query);
248
4/4
✓ Branch 23 → 24 taken 2 times.
✓ Branch 23 → 29 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
✓ Branch 27 → 28 taken 2 times.
4 if (term.kind == Kind::Text) { parsed.text = value.trimmed(); return build(parsed); }
249
2/4
✓ Branch 29 → 30 taken 2 times.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 33 taken 2 times.
✗ Branch 31 → 32 not taken.
2 if (!erase(parsed, term)) return build(parsed);
250
2/2
✓ Branch 33 → 34 taken 2 times.
✓ Branch 34 → 35 taken 2 times.
2 insert(parsed, term.kind, value.trimmed(), term.negated);
251
1/1
✓ Branch 36 → 37 taken 2 times.
2 return build(parsed);
252 8 }
253
254 8 QString negate(const QString &query, int index) {
255
1/1
✓ Branch 2 → 3 taken 8 times.
16 const auto all = terms(query);
256
3/7
✓ Branch 8 → 9 taken 8 times.
✗ Branch 8 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 8 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 16 taken 8 times.
✗ Branch 14 → 15 not taken.
8 if (index < 0 || index >= all.size()) return canonical(query);
257 8 const Term &term = all.at(index);
258
1/1
✓ Branch 17 → 18 taken 8 times.
8 Parsed parsed = parse(query);
259
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 25 taken 7 times.
8 if (Tri *state = stateFor(parsed.filter, term.kind)) {
260
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
1 *state = *state == Tri::Yes ? Tri::No : Tri::Yes;
261
1/1
✓ Branch 23 → 24 taken 1 time.
1 return build(parsed);
262 }
263
5/10
✓ Branch 25 → 26 taken 7 times.
✗ Branch 25 → 29 not taken.
✓ Branch 26 → 27 taken 7 times.
✗ Branch 26 → 29 not taken.
✓ Branch 27 → 28 taken 7 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 7 times.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 34 taken 7 times.
✗ Branch 32 → 33 not taken.
7 if (term.kind == Kind::Text || term.kind == Kind::Date || !erase(parsed, term)) return build(parsed);
264
5/5
✓ Branch 34 → 35 taken 1 time.
✓ Branch 34 → 45 taken 6 times.
✓ Branch 37 → 38 taken 1 time.
✓ Branch 38 → 39 taken 1 time.
✓ Branch 39 → 40 taken 1 time.
7 if (term.kind == Kind::ExcludedText) parsed.text = (parsed.text + QLatin1Char(' ') + term.value).trimmed();
265
1/1
✓ Branch 45 → 46 taken 6 times.
6 else insert(parsed, term.kind, term.value, !term.negated);
266
1/1
✓ Branch 46 → 47 taken 7 times.
7 return build(parsed);
267 16 }
268
269 22 QString add(const QString &query, Kind kind, const QString &value, bool negated) {
270
1/1
✓ Branch 2 → 3 taken 22 times.
22 Parsed parsed = parse(query);
271
1/5
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 14 taken 22 times.
✗ Branch 6 → 7 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 8 → 9 not taken.
22 if (kind == Kind::Text) parsed.text = (parsed.text + QLatin1Char(' ') + value).trimmed();
272
1/1
✓ Branch 14 → 15 taken 22 times.
22 else insert(parsed, kind, value, negated);
273
1/1
✓ Branch 15 → 16 taken 22 times.
44 return build(parsed);
274 22 }
275
276 44 QString setDateRange(const QString &query, const QDate &from, const QDate &to) {
277
1/1
✓ Branch 2 → 3 taken 44 times.
44 Parsed parsed = parse(query);
278 44 auto &f = parsed.filter;
279 44 f.datePreset.clear();
280
5/5
✓ Branch 4 → 5 taken 44 times.
✓ Branch 5 → 6 taken 40 times.
✓ Branch 5 → 8 taken 4 times.
✓ Branch 6 → 7 taken 40 times.
✓ Branch 7 → 9 taken 40 times.
44 f.dateFrom = from.isValid() ? QDateTime(from, QTime(0, 0)) : QDateTime();
281
5/5
✓ Branch 11 → 12 taken 44 times.
✓ Branch 12 → 13 taken 40 times.
✓ Branch 12 → 15 taken 4 times.
✓ Branch 13 → 14 taken 40 times.
✓ Branch 14 → 16 taken 40 times.
44 f.dateTo = to.isValid() ? QDateTime(to, QTime(23, 59, 59)) : QDateTime();
282
1/1
✓ Branch 18 → 19 taken 44 times.
88 return build(parsed);
283 44 }
284
285 2 QString setDatePreset(const QString &query, const QString &preset) {
286
1/1
✓ Branch 2 → 3 taken 2 times.
2 Parsed parsed = parse(query);
287 2 auto &f = parsed.filter;
288 2 f.dateFrom = {}; f.dateTo = {}; f.datePreset.clear();
289
1/1
✓ Branch 10 → 11 taken 2 times.
2 const QString base = build(parsed);
290
6/7
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 17 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 22 taken 1 time.
2 if (preset.isEmpty() || preset.contains(QLatin1Char(' '))) return base;
291
3/3
✓ Branch 25 → 26 taken 1 time.
✓ Branch 26 → 27 taken 1 time.
✓ Branch 27 → 28 taken 1 time.
1 const Parsed withDate = parse(base + QStringLiteral(" date:") + preset);
292
2/3
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
1 return withDate.filter.datePreset.isEmpty() ? base : build(withDate);
293 2 }
294
295
1/1
✓ Branch 2 → 3 taken 9 times.
9 QString datePreset(const QString &query) { return parse(query).filter.datePreset; }
296
297 9 QString setState(const QString &query, Kind kind, int value) {
298
1/1
✓ Branch 2 → 3 taken 9 times.
9 Parsed parsed = parse(query);
299
5/6
✓ Branch 4 → 5 taken 9 times.
✗ Branch 4 → 12 not taken.
✓ Branch 5 → 6 taken 3 times.
✓ Branch 5 → 10 taken 6 times.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 2 times.
9 if (Tri *state = stateFor(parsed.filter, kind)) *state = value > 0 ? Tri::Yes : value == 0 ? Tri::No : Tri::Any;
300
1/1
✓ Branch 12 → 13 taken 9 times.
18 return build(parsed);
301 9 }
302
303 540 int state(const QString &query, Kind kind) {
304
1/1
✓ Branch 2 → 3 taken 540 times.
540 Parsed parsed = parse(query);
305 540 const Tri *value = stateFor(parsed.filter, kind);
306
5/6
✓ Branch 4 → 5 taken 540 times.
✗ Branch 4 → 10 not taken.
✓ Branch 5 → 6 taken 29 times.
✓ Branch 5 → 10 taken 511 times.
✓ Branch 6 → 7 taken 26 times.
✓ Branch 6 → 8 taken 3 times.
1080 return !value || *value == Tri::Any ? -1 : *value == Tri::Yes ? 1 : 0;
307 540 }
308
309 4 QString setStatusStates(const QString &query, bool unread, bool flagged, bool attachment) {
310
1/1
✓ Branch 2 → 3 taken 4 times.
4 Parsed parsed = parse(query);
311 4 const std::pair<Kind, bool> chips[] = {{Kind::Unread, unread}, {Kind::Flagged, flagged}, {Kind::Attachment, attachment}};
312
2/2
✓ Branch 17 → 7 taken 12 times.
✓ Branch 17 → 18 taken 4 times.
16 for (const auto &[kind, wanted] : chips) {
313 12 Tri *state = stateFor(parsed.filter, kind);
314
3/4
✓ Branch 10 → 11 taken 12 times.
✗ Branch 10 → 13 not taken.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 10 times.
12 if (state && wanted) *state = Tri::Yes;
315
3/4
✓ Branch 13 → 14 taken 10 times.
✗ Branch 13 → 16 not taken.
✓ Branch 14 → 15 taken 4 times.
✓ Branch 14 → 16 taken 6 times.
10 else if (state && *state == Tri::Yes) *state = Tri::Any;
316 }
317
1/1
✓ Branch 18 → 19 taken 4 times.
8 return build(parsed);
318 4 }
319
320
2/2
✓ Branch 2 → 3 taken 1560 times.
✓ Branch 3 → 4 taken 1560 times.
1560 QDate dateFrom(const QString &query) { return parse(query).filter.dateFrom.date(); }
321
2/2
✓ Branch 2 → 3 taken 1449 times.
✓ Branch 3 → 4 taken 1449 times.
1449 QDate dateTo(const QString &query) { return parse(query).filter.dateTo.date(); }
322
323 391 QString title(const QString &query, const Lookups &lookups, const QLocale &locale) {
324 391 QStringList parts;
325
5/5
✓ Branch 2 → 3 taken 391 times.
✓ Branch 3 → 4 taken 391 times.
✓ Branch 4 → 5 taken 391 times.
✓ Branch 14 → 6 taken 558 times.
✓ Branch 14 → 15 taken 391 times.
949 for (const Term &term : terms(query, lookups, locale))
326
4/5
✓ Branch 7 → 8 taken 14 times.
✓ Branch 7 → 10 taken 544 times.
✓ Branch 8 → 9 taken 14 times.
✗ Branch 8 → 10 not taken.
✓ Branch 11 → 12 taken 558 times.
949 parts.append(term.negated && term.kind != Kind::ExcludedText ? term.label : term.shortLabel);
327
8/12
✓ Branch 17 → 18 taken 8 times.
✓ Branch 17 → 19 taken 383 times.
✓ Branch 18 → 23 taken 8 times.
✓ Branch 22 → 23 taken 383 times.
✓ Branch 24 → 25 taken 383 times.
✓ Branch 24 → 26 taken 8 times.
✓ Branch 26 → 27 taken 383 times.
✓ Branch 26 → 28 taken 8 times.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 36 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
1165 return parts.isEmpty() ? tr("Search") : parts.join(QStringLiteral(" · "));
328 391 }
329
330 649 QString relaxationLabel(const Term &term) {
331
10/15
✓ Branch 2 → 3 taken 330 times.
✗ Branch 2 → 4 not taken.
✓ Branch 2 → 9 taken 138 times.
✗ Branch 2 → 10 not taken.
✓ Branch 2 → 11 taken 40 times.
✓ Branch 2 → 12 taken 10 times.
✓ Branch 2 → 13 taken 2 times.
✓ Branch 2 → 14 taken 2 times.
✓ Branch 2 → 15 taken 75 times.
✓ Branch 2 → 16 taken 32 times.
✓ Branch 2 → 17 taken 3 times.
✗ Branch 2 → 18 not taken.
✓ Branch 2 → 19 taken 17 times.
✗ Branch 2 → 20 not taken.
✗ Branch 2 → 21 not taken.
649 switch (term.kind) {
332 330 case Kind::Text: return tr("Any text");
333 ✗ case Kind::ExcludedText: return tr("Also with %1").arg(term.shortLabel);
334 138 case Kind::From: return tr("Any sender");
335 ✗ case Kind::To: return tr("Any recipient");
336 40 case Kind::Folder: return tr("Any folder");
337 10 case Kind::Subject: return tr("Any subject");
338 2 case Kind::List: return tr("Any list");
339 2 case Kind::Label: return tr("Any label");
340 75 case Kind::Date: return tr("Any time");
341 32 case Kind::Unread: return tr("Read and unread");
342 3 case Kind::Flagged: return tr("Any importance");
343 ✗ case Kind::Answered: return tr("Answered or not");
344 17 case Kind::Attachment: return tr("With or without attachment");
345 ✗ case Kind::About: return tr("About anyone");
346 }
347 ✗ return {};
348 }
349
350 15 QList<Relaxation> relaxations(const QString &query, const Lookups &lookups) {
351 15 QList<Relaxation> result;
352
1/1
✓ Branch 2 → 3 taken 15 times.
15 const Parsed parsed = parse(query);
353
1/1
✓ Branch 6 → 7 taken 15 times.
15 const QStringList words = parsed.text.split(QLatin1Char(' '), Qt::SkipEmptyParts);
354
2/2
✓ Branch 30 → 9 taken 18 times.
✓ Branch 30 → 31 taken 15 times.
33 for (int i = int(words.size()) - 1; i >= 0; --i) {
355 18 Parsed without = parsed;
356 18 QStringList rest = words;
357
1/1
✓ Branch 11 → 12 taken 18 times.
18 rest.removeAt(i);
358
1/1
✓ Branch 14 → 15 taken 18 times.
18 without.text = rest.join(QLatin1Char(' '));
359
2/2
✓ Branch 17 → 18 taken 18 times.
✓ Branch 20 → 21 taken 18 times.
54 result.append({tr("Without %1").arg(quoted(words.at(i))), build(without)});
360 18 }
361
6/6
✓ Branch 31 → 32 taken 15 times.
✓ Branch 32 → 33 taken 15 times.
✓ Branch 34 → 35 taken 15 times.
✓ Branch 35 → 36 taken 15 times.
✓ Branch 46 → 37 taken 28 times.
✓ Branch 46 → 47 taken 15 times.
43 for (const Term &term : terms(query, lookups))
362
2/2
✓ Branch 38 → 39 taken 15 times.
✓ Branch 38 → 44 taken 13 times.
58 if (term.kind != Kind::Text) result.append({relaxationLabel(term), remove(query, term.index)});
363 15 return result;
364 66 }
365
366 47 QString kindLabel(Kind kind) {
367
6/9
✓ Branch 2 → 3 taken 9 times.
✓ Branch 2 → 4 taken 9 times.
✓ Branch 2 → 5 taken 9 times.
✗ Branch 2 → 6 not taken.
✓ Branch 2 → 7 taken 10 times.
✓ Branch 2 → 8 taken 9 times.
✗ Branch 2 → 9 not taken.
✗ Branch 2 → 10 not taken.
✓ Branch 2 → 11 taken 1 time.
47 switch (kind) {
368 9 case Kind::From: return tr("From");
369 9 case Kind::To: return tr("To");
370 9 case Kind::Folder: return tr("Folder");
371 ✗ case Kind::Subject: return tr("Subject");
372 10 case Kind::List: return tr("Mailing list");
373 9 case Kind::Label: return tr("Label");
374 ✗ case Kind::Date: return tr("Date");
375 ✗ case Kind::About: return tr("About");
376 1 default: return {};
377 }
378 }
379
380 13714 QString kindName(Kind kind) {
381
14/15
✓ Branch 2 → 3 taken 1919 times.
✓ Branch 2 → 8 taken 1588 times.
✓ Branch 2 → 13 taken 2310 times.
✓ Branch 2 → 18 taken 1111 times.
✓ Branch 2 → 23 taken 1136 times.
✓ Branch 2 → 28 taken 1056 times.
✓ Branch 2 → 33 taken 1083 times.
✓ Branch 2 → 38 taken 1598 times.
✓ Branch 2 → 43 taken 599 times.
✓ Branch 2 → 48 taken 569 times.
✓ Branch 2 → 53 taken 354 times.
✓ Branch 2 → 58 taken 192 times.
✓ Branch 2 → 63 taken 198 times.
✓ Branch 2 → 68 taken 1 time.
✗ Branch 2 → 73 not taken.
13714 switch (kind) {
382 3838 case Kind::Text: return QStringLiteral("text");
383 3176 case Kind::ExcludedText: return QStringLiteral("excludedText");
384 4620 case Kind::From: return QStringLiteral("from");
385 2222 case Kind::To: return QStringLiteral("to");
386 2272 case Kind::Folder: return QStringLiteral("folder");
387 2112 case Kind::Subject: return QStringLiteral("subject");
388 2166 case Kind::List: return QStringLiteral("list");
389 3196 case Kind::Label: return QStringLiteral("label");
390 1198 case Kind::Date: return QStringLiteral("date");
391 1138 case Kind::Unread: return QStringLiteral("unread");
392 708 case Kind::Flagged: return QStringLiteral("flagged");
393 384 case Kind::Answered: return QStringLiteral("answered");
394 396 case Kind::Attachment: return QStringLiteral("attachment");
395 2 case Kind::About: return QStringLiteral("about");
396 }
397 ✗ return {};
398 }
399
400 1588 Kind kindFromName(const QString &name) {
401 11769 for (const Kind kind : {Kind::Text, Kind::ExcludedText, Kind::From, Kind::To, Kind::Folder, Kind::Subject,
402 Kind::List, Kind::Label, Kind::Date, Kind::Unread, Kind::Flagged, Kind::Answered,
403
2/2
✓ Branch 11 → 5 taken 11768 times.
✓ Branch 11 → 12 taken 1 time.
13357 Kind::Attachment, Kind::About})
404
3/3
✓ Branch 5 → 6 taken 11768 times.
✓ Branch 8 → 9 taken 1587 times.
✓ Branch 8 → 10 taken 10181 times.
11768 if (kindName(kind) == name) return kind;
405 1 return Kind::Text;
406 }
407
408 } // namespace SearchTermModel
409