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: 64.6% 62 / 0 / 96
Functions: 71.4% 10 / 0 / 14
Branches: 63.2% 72 / 0 / 114

presentation/QuickContactMailHistory.cpp
Line Branch Exec Source
1 #include "QuickContactMailHistory.h"
2
3 #include "data/MailAddressIndex.h"
4 #include "service/MailSearchReadService.h"
5
6 #include <QDate>
7 #include <QDateTime>
8 #include <QElapsedTimer>
9 #include <QLocale>
10 #include <QLoggingCategory>
11 #include <QSet>
12 #include <algorithm>
13
14
4/7
✓ Branch 2 → 3 taken 11 times.
✓ Branch 2 → 8 taken 250 times.
✓ Branch 4 → 5 taken 11 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 11 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
261 Q_LOGGING_CATEGORY(lcContactMailHistory, "mailjd.contacts.mails")
15
16 namespace {
17
18 127 QString dateText(const QDateTime &when) {
19
1/1
✓ Branch 2 → 3 taken 127 times.
127 const QDateTime local = when.toLocalTime();
20
1/1
✓ Branch 3 → 4 taken 127 times.
127 const QDate today = QDate::currentDate();
21
2/3
✓ Branch 4 → 5 taken 127 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 13 taken 127 times.
127 if (local.date() == today)
22 ✗ return QLocale().toString(local.time(), QLocale::ShortFormat);
23
3/4
✓ Branch 13 → 14 taken 127 times.
✓ Branch 14 → 15 taken 127 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 127 times.
127 if (local.date() == today.addDays(-1))
24 ✗ return QuickContactMailHistory::tr("Yesterday");
25
3/3
✓ Branch 19 → 20 taken 127 times.
✓ Branch 20 → 21 taken 127 times.
✓ Branch 21 → 22 taken 127 times.
127 return QLocale().toString(local.date(), QLocale::ShortFormat);
26 127 }
27
28 127 QString folderName(const QString &path) {
29
2/2
✓ Branch 5 → 6 taken 127 times.
✓ Branch 9 → 10 taken 127 times.
254 const QString name = path.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), -1);
30
3/3
✓ Branch 13 → 14 taken 45 times.
✓ Branch 13 → 15 taken 82 times.
✓ Branch 14 → 16 taken 45 times.
254 return name.compare(QLatin1String("INBOX"), Qt::CaseInsensitive) == 0 ? QuickContactMailHistory::tr("Inbox") : name;
31 127 }
32
33 } // namespace
34
35 796 QuickContactMailHistory::QuickContactMailHistory(DatabasePath databasePath, QObject *parent)
36
1/1
✓ Branch 5 → 6 taken 796 times.
796 : QObject(parent), m_databasePath(std::move(databasePath)) {
37
1/1
✓ Branch 12 → 13 taken 796 times.
796 m_pool.setMaxThreadCount(1);
38 // One long-lived worker thread: its kept-open connections (Phase 5) live
39 // in thread-local storage and must not be recreated per request.
40
1/1
✓ Branch 13 → 14 taken 796 times.
796 m_pool.setExpiryTimeout(-1);
41 796 }
42
43 796 QuickContactMailHistory::~QuickContactMailHistory() { m_pool.waitForDone(); }
44
45 6 QVariantList QuickContactMailHistory::readByLike(const QString &databasePath, const QStringList &addresses, int limit) {
46 6 QList<MailSearchHit> hits;
47 6 QSet<QString> seen;
48 6 const std::atomic_bool cancelled = false;
49
5/5
✓ Branch 3 → 4 taken 6 times.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 5 → 6 taken 6 times.
✓ Branch 42 → 7 taken 10 times.
✓ Branch 42 → 43 taken 6 times.
16 for (const QString &address : addresses.mid(0, MaxAddresses)) {
50
2/2
✓ Branch 39 → 10 taken 20 times.
✓ Branch 39 → 40 taken 10 times.
30 for (const bool sent : {false, true}) {
51 20 MailSearchRequest request;
52 20 request.databasePath = databasePath;
53
2/2
✓ Branch 12 → 13 taken 10 times.
✓ Branch 12 → 14 taken 10 times.
20 (sent ? request.filter.toFilter : request.filter.fromFilter) = address;
54 20 request.limit = limit * 2;
55
1/1
✓ Branch 15 → 16 taken 20 times.
20 const MailSearchPage page = MailSearchReadService::readCache(request, cancelled);
56
2/2
✓ Branch 35 → 18 taken 677 times.
✓ Branch 35 → 36 taken 20 times.
697 for (const MailSearchHit &hit : page.hits) {
57
4/4
✓ Branch 19 → 20 taken 677 times.
✓ Branch 22 → 23 taken 677 times.
✓ Branch 23 → 24 taken 677 times.
✓ Branch 24 → 25 taken 677 times.
677 const QString id = QString::number(hit.header.folderId) + QLatin1Char('/') + QString::number(hit.header.uid);
58
2/2
✓ Branch 29 → 30 taken 622 times.
✓ Branch 29 → 32 taken 55 times.
677 if (!seen.contains(id)) {
59
1/1
✓ Branch 30 → 31 taken 622 times.
622 seen.insert(id);
60
1/1
✓ Branch 31 → 32 taken 622 times.
622 hits.append(hit);
61 }
62 677 }
63 20 }
64 6 }
65 // S106-279: the search matches substrings (joann@ for ann@); a mail
66 // counts only when one of its address tokens is the person's address,
67 // and "sent" means none of its senders is — as in MailAddressHistory.
68 6 QSet<QString> keys;
69
5/5
✓ Branch 45 → 46 taken 6 times.
✓ Branch 46 → 47 taken 6 times.
✓ Branch 47 → 48 taken 6 times.
✓ Branch 58 → 49 taken 10 times.
✓ Branch 58 → 59 taken 6 times.
16 for (const QString &address : addresses.mid(0, MaxAddresses))
70
2/3
✓ Branch 50 → 51 taken 10 times.
✓ Branch 52 → 53 taken 10 times.
✗ Branch 52 → 55 not taken.
10 if (const QString key = MailAddressIndex::normalize(address); !key.isEmpty())
71
1/1
✓ Branch 53 → 54 taken 10 times.
16 keys.insert(key);
72 931 const auto named = [&keys](const QString &header) {
73
1/1
✓ Branch 2 → 3 taken 931 times.
931 const QStringList tokens = MailAddressIndex::parse(header);
74
1/1
✓ Branch 5 → 6 taken 931 times.
2792 return std::any_of(tokens.cbegin(), tokens.cend(), [&keys](const QString &token) { return keys.contains(token); });
75 931 };
76
1/1
✓ Branch 60 → 61 taken 6 times.
6 hits.removeIf([&named](const MailSearchHit &hit) {
77
5/6
✓ Branch 3 → 4 taken 181 times.
✓ Branch 3 → 9 taken 441 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 9 taken 180 times.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 9 not taken.
622 return !named(hit.header.from) && !named(hit.header.to) && !named(hit.header.cc);
78 });
79
3/3
✓ Branch 61 → 62 taken 6 times.
✓ Branch 62 → 63 taken 6 times.
✓ Branch 63 → 64 taken 6 times.
6 std::sort(hits.begin(), hits.end(), [](const MailSearchHit &a, const MailSearchHit &b) {
80 6212 return a.header.date > b.header.date;
81 });
82 6 QVariantList result;
83
5/5
✓ Branch 64 → 65 taken 6 times.
✓ Branch 65 → 66 taken 6 times.
✓ Branch 66 → 67 taken 6 times.
✓ Branch 126 → 68 taken 127 times.
✓ Branch 126 → 127 taken 6 times.
133 for (const MailSearchHit &hit : hits.mid(0, limit)) {
84
1/1
✓ Branch 69 → 70 taken 127 times.
127 const bool sent = !named(hit.header.from);
85
1/1
✓ Branch 102 → 103 taken 127 times.
381 result.append(QVariantMap{{QStringLiteral("subject"), hit.header.subject},
86 254 {QStringLiteral("from"), hit.header.from},
87
1/1
✓ Branch 81 → 82 taken 127 times.
254 {QStringLiteral("dateText"), dateText(hit.header.date)},
88
1/1
✓ Branch 86 → 87 taken 127 times.
254 {QStringLiteral("folder"), folderName(hit.folderPath)},
89 254 {QStringLiteral("messageId"), hit.header.messageId},
90 254 {QStringLiteral("date"), hit.header.date},
91
2/4
✓ Branch 106 → 107 taken 889 times.
✓ Branch 106 → 108 taken 127 times.
✗ Branch 170 → 171 not taken.
✗ Branch 170 → 172 not taken.
1270 {QStringLiteral("sent"), sent}});
92 6 }
93 6 return result;
94 1022 }
95
96 ✗ void QuickContactMailHistory::request(const QStringList &addresses) {
97 ✗ const quint64 generation = ++m_generation;
98 ✗ const QString path = m_databasePath ? m_databasePath() : QString();
99 ✗ if (path.isEmpty() || addresses.isEmpty()) {
100 ✗ clear();
101 ✗ return;
102 }
103 ✗ m_loading = true;
104 ✗ emit mailsChanged();
105 ✗ m_pool.start([this, generation, path, addresses] {
106 ✗ QElapsedTimer timer;
107 ✗ timer.start();
108 ✗ const QVariantList mails = read(path, addresses);
109 ✗ qCDebug(lcContactMailHistory) << "Recent mails:" << mails.size() << "for" << addresses.size() << "addresses in"
110 ✗ << timer.elapsed() << "ms";
111 ✗ QMetaObject::invokeMethod(
112 this,
113 ✗ [this, generation, mails] {
114 ✗ if (generation != m_generation)
115 ✗ return;
116 ✗ m_mails = mails;
117 ✗ m_loading = false;
118 ✗ emit mailsChanged();
119 },
120 Qt::QueuedConnection);
121 ✗ });
122 ✗ }
123
124 ✗ void QuickContactMailHistory::clear() {
125 ✗ ++m_generation;
126 ✗ m_mails.clear();
127 ✗ m_items.clear();
128 ✗ m_lastSeen.clear();
129 ✗ m_loading = false;
130 ✗ emit mailsChanged();
131 ✗ }
132