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: 100.0% 60 / 0 / 60
Functions: 100.0% 6 / 0 / 6
Branches: 91.9% 57 / 0 / 62

service/NotificationBatcher.cpp
Line Branch Exec Source
1 #include "NotificationBatcher.h"
2
3 #include <QLoggingCategory>
4 #include <QStringList>
5 #include <limits>
6 #include <utility>
7
8
4/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 8 taken 23 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
25 Q_LOGGING_CATEGORY(lcBatcher, "mailjd.notificationbatcher")
9
10 namespace {
11 constexpr int kDefaultDebounceMs = 3000;
12 constexpr int kMaximumDelayIntervals = 5; // S106-214: a burst waits at most this many
13 constexpr int kSummaryThreshold = 3; // >= this many mails → one summary
14 constexpr int kSummaryPreviewLines = 3;
15 } // namespace
16
17
1/1
✓ Branch 4 → 5 taken 808 times.
808 NotificationBatcher::NotificationBatcher(QObject *parent) : QObject(parent) {
18
1/1
✓ Branch 6 → 7 taken 808 times.
808 m_debounce.setSingleShot(true);
19 808 m_debounceMs = kDefaultDebounceMs;
20
1/1
✓ Branch 7 → 8 taken 808 times.
808 connect(&m_debounce, &QTimer::timeout, this, &NotificationBatcher::flush);
21 808 }
22
23 17 void NotificationBatcher::setDebounceMs(int ms) {
24 17 m_debounceMs = qMax(0, ms);
25 17 }
26
27 81 void NotificationBatcher::addPending(const QString &from,
28 const QString &subject, qint64 uid,
29 qint64 folderId) {
30
1/2
✓ Branch 3 → 4 taken 81 times.
✗ Branch 3 → 5 not taken.
81 if (m_pendingCount < std::numeric_limits<int>::max()) ++m_pendingCount;
31
2/2
✓ Branch 6 → 7 taken 50 times.
✓ Branch 6 → 12 taken 31 times.
81 if (m_preview.size() < kSummaryPreviewLines)
32 50 m_preview.append({from, subject, uid, folderId});
33 // S106-214: UIDs order mails within one folder only; across folders the
34 // later arrival is the newer mail.
35
6/6
✓ Branch 12 → 13 taken 60 times.
✓ Branch 12 → 15 taken 21 times.
✓ Branch 13 → 14 taken 59 times.
✓ Branch 13 → 15 taken 1 time.
✓ Branch 14 → 15 taken 58 times.
✓ Branch 14 → 16 taken 1 time.
81 if (m_pendingCount == 1 || folderId != m_newestFolderId || uid > m_newestUid) {
36 80 m_newestUid = uid;
37 80 m_newestFolderId = folderId;
38 }
39
2/2
✓ Branch 16 → 17 taken 18 times.
✓ Branch 16 → 18 taken 63 times.
81 if (m_suppressed)
40 18 return; // bounded summary state until the first INBOX sync completes
41 // Restart: IMAP delivers headers in chunks. A steady stream no longer
42 // defers the notification without end (S106-214).
43
2/2
✓ Branch 19 → 20 taken 18 times.
✓ Branch 19 → 21 taken 45 times.
63 if (!m_burst.isValid()) m_burst.start();
44 63 const qint64 left = qint64(m_debounceMs) * kMaximumDelayIntervals - m_burst.elapsed();
45
1/1
✓ Branch 23 → 24 taken 63 times.
63 m_debounce.start(int(qBound(qint64(0), left, qint64(m_debounceMs))));
46 50 }
47
48 18 void NotificationBatcher::setSyncComplete(bool dropBuffered) {
49 18 m_suppressed = false;
50
2/2
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 19 taken 12 times.
18 if (dropBuffered) {
51
5/5
✓ Branch 3 → 4 taken 6 times.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 8 → 9 taken 6 times.
✓ Branch 14 → 5 taken 6 times.
✓ Branch 14 → 15 taken 6 times.
12 qCInfo(lcBatcher) << "Initial mailbox load — dropping"
52
2/2
✓ Branch 9 → 10 taken 6 times.
✓ Branch 10 → 11 taken 6 times.
6 << m_pendingCount << "buffered notifications";
53 6 m_debounce.stop();
54 6 m_burst.invalidate();
55 6 m_preview.clear();
56 6 m_pendingCount = 0;
57 6 m_newestUid = m_newestFolderId = -1;
58 6 return;
59 }
60
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 21 taken 11 times.
12 if (m_pendingCount > 0)
61 1 flush();
62 }
63
64 19 void NotificationBatcher::flush() {
65
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 19 times.
19 if (m_pendingCount == 0)
66 7 return;
67 19 const int count = m_pendingCount;
68 19 const QList<PendingMail> preview = std::move(m_preview);
69 19 const qint64 newestUid = m_newestUid;
70 19 const qint64 newestFolderId = m_newestFolderId;
71
1/1
✓ Branch 6 → 7 taken 19 times.
19 m_preview.clear();
72 19 m_pendingCount = 0;
73 19 m_newestUid = m_newestFolderId = -1;
74 19 m_burst.invalidate();
75
1/1
✓ Branch 8 → 9 taken 19 times.
19 m_debounce.stop();
76
77
7/7
✓ Branch 9 → 10 taken 19 times.
✓ Branch 13 → 14 taken 19 times.
✓ Branch 14 → 15 taken 19 times.
✓ Branch 15 → 16 taken 19 times.
✓ Branch 16 → 17 taken 19 times.
✓ Branch 20 → 11 taken 19 times.
✓ Branch 20 → 21 taken 19 times.
38 qCInfo(lcBatcher) << "Flushing" << count << "pending notifications";
78
79
2/2
✓ Branch 21 → 22 taken 7 times.
✓ Branch 21 → 30 taken 12 times.
19 if (count < kSummaryThreshold) {
80
2/2
✓ Branch 28 → 24 taken 8 times.
✓ Branch 28 → 29 taken 7 times.
15 for (const auto &m : preview)
81
1/1
✓ Branch 25 → 26 taken 8 times.
8 emit notifyIndividual(m.from, m.subject, m.uid, m.folderId);
82 7 return;
83 }
84
85 // Summary: first senders + subjects, then "…"
86 12 QStringList lines;
87
2/2
✓ Branch 43 → 32 taken 36 times.
✓ Branch 43 → 44 taken 12 times.
48 for (const auto &mail : preview)
88
2/2
✓ Branch 36 → 37 taken 36 times.
✓ Branch 37 → 38 taken 36 times.
36 lines << QStringLiteral("%1 — %2").arg(mail.from, mail.subject);
89
2/2
✓ Branch 44 → 45 taken 8 times.
✓ Branch 44 → 52 taken 4 times.
12 if (count > kSummaryPreviewLines)
90
1/1
✓ Branch 48 → 49 taken 8 times.
8 lines << QStringLiteral("…");
91
92 // Newest mail: see addPending().
93
2/2
✓ Branch 55 → 56 taken 12 times.
✓ Branch 56 → 57 taken 12 times.
12 emit notifySummary(tr("%n new mails", "", count),
94
1/1
✓ Branch 54 → 55 taken 12 times.
24 lines.join(QLatin1Char('\n')), count,
95 newestFolderId, newestUid);
96 19 }
97