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% 86 / 0 / 86
Functions: 100.0% 13 / 0 / 13
Branches: 89.0% 105 / 0 / 118

controller/UndoManager.cpp
Line Branch Exec Source
1 #include "UndoManager.h"
2
3 #include <QDateTime>
4 #include <QLoggingCategory>
5 #include <QPointer>
6 #include <algorithm>
7
8
4/7
✓ Branch 2 → 3 taken 13 times.
✓ Branch 2 → 8 taken 507 times.
✓ Branch 4 → 5 taken 13 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 13 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
520 Q_LOGGING_CATEGORY(lcUndo, "mailjd.undo")
9
10 1051 UndoManager::UndoManager(QObject *parent) : QObject(parent) {}
11
12 359 void UndoManager::push(const QString &description,
13 std::function<void()> undoAction) {
14 359 UndoEntry entry;
15 359 entry.description = description;
16 359 entry.undo = std::move(undoAction);
17
1/1
✓ Branch 8 → 9 taken 359 times.
359 append(std::move(entry));
18 359 }
19
20 37 bool UndoManager::pushAsync(const QString &description,
21 std::function<void(Completion)> undoAction,
22 qint64 retainedBytes) {
23
8/8
✓ Branch 3 → 4 taken 36 times.
✓ Branch 3 → 6 taken 1 time.
✓ Branch 4 → 5 taken 35 times.
✓ Branch 4 → 6 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 34 times.
✓ Branch 8 → 9 taken 3 times.
✓ Branch 8 → 10 taken 34 times.
37 if (!undoAction || retainedBytes < 0 || retainedBytes > kMaxAsyncRetainedBytes) return false;
24 34 UndoEntry entry;
25 34 entry.description = description;
26 34 entry.asynchronousUndo = std::move(undoAction);
27 34 entry.retainedBytes = retainedBytes;
28
1/1
✓ Branch 16 → 17 taken 34 times.
34 return append(std::move(entry));
29 34 }
30
31 393 bool UndoManager::append(UndoEntry entry) {
32 393 qint64 retainedBytes = entry.retainedBytes;
33 393 qint64 activeBytes = 0;
34
2/2
✓ Branch 10 → 5 taken 5140 times.
✓ Branch 10 → 11 taken 393 times.
5533 for (const auto &existing : std::as_const(m_stack)) {
35 5140 retainedBytes += existing.retainedBytes;
36
2/2
✓ Branch 6 → 7 taken 61 times.
✓ Branch 6 → 8 taken 5079 times.
5140 if (existing.id == m_activeId) activeBytes = existing.retainedBytes;
37 }
38
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 392 times.
393 if (activeBytes + entry.retainedBytes > kMaxAsyncRetainedBytes) return false;
39 392 entry.timestamp = QDateTime::currentMSecsSinceEpoch();
40 392 entry.id = ++m_nextId;
41
42 392 m_stack.append(std::move(entry));
43
44 // Enforce max depth — discard oldest entries
45
6/6
✓ Branch 24 → 25 taken 393 times.
✓ Branch 24 → 26 taken 26 times.
✓ Branch 25 → 26 taken 1 time.
✓ Branch 25 → 27 taken 392 times.
✓ Branch 28 → 17 taken 27 times.
✓ Branch 28 → 29 taken 392 times.
419 while (m_stack.size() > kMaxUndoDepth || retainedBytes > kMaxAsyncRetainedBytes) {
46 // An accepted asynchronous operation retains its entry until completion.
47
2/2
✓ Branch 18 → 19 taken 11 times.
✓ Branch 18 → 20 taken 16 times.
27 const int oldest = m_stack.first().id == m_activeId ? 1 : 0;
48 27 retainedBytes -= m_stack.at(oldest).retainedBytes;
49 27 m_stack.removeAt(oldest);
50 }
51
52
7/7
✓ Branch 29 → 30 taken 392 times.
✓ Branch 33 → 34 taken 392 times.
✓ Branch 34 → 35 taken 392 times.
✓ Branch 35 → 36 taken 392 times.
✓ Branch 36 → 37 taken 392 times.
✓ Branch 44 → 31 taken 392 times.
✓ Branch 44 → 45 taken 392 times.
784 qCInfo(lcUndo) << "Push:" << m_stack.last().description
53
3/3
✓ Branch 37 → 38 taken 392 times.
✓ Branch 39 → 40 taken 392 times.
✓ Branch 40 → 41 taken 392 times.
392 << "(stack size:" << m_stack.size() << ")";
54 392 emit undoAvailable(canUndo());
55 392 return true;
56 }
57
58 43 bool UndoManager::undo() {
59
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 15 taken 41 times.
43 if (!canUndo()) {
60
5/5
✓ Branch 4 → 5 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 13 → 6 taken 2 times.
✓ Branch 13 → 14 taken 2 times.
4 qCInfo(lcUndo) << "Undo: stack empty";
61 2 return false;
62 }
63
64
3/3
✓ Branch 15 → 16 taken 41 times.
✓ Branch 17 → 18 taken 16 times.
✓ Branch 17 → 39 taken 25 times.
41 if (m_stack.last().asynchronousUndo) {
65
2/2
✓ Branch 18 → 19 taken 16 times.
✓ Branch 19 → 20 taken 16 times.
16 const auto entry = m_stack.last();
66 16 m_activeId = entry.id;
67 16 const auto attempt = m_activeAttempt = ++m_nextAttempt;
68
1/1
✓ Branch 20 → 21 taken 16 times.
16 const QPointer<UndoManager> owner(this);
69
1/1
✓ Branch 21 → 22 taken 16 times.
16 emit undoAvailable(false);
70
3/6
✓ Branch 23 → 24 taken 16 times.
✗ Branch 23 → 26 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 16 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 16 times.
16 if (!owner || owner->m_activeId != entry.id) return true;
71
2/4
✓ Branch 31 → 32 taken 16 times.
✓ Branch 32 → 33 taken 16 times.
✗ Branch 79 → 80 not taken.
✗ Branch 79 → 81 not taken.
16 entry.asynchronousUndo([owner, id = entry.id, attempt](bool succeeded) {
72
2/2
✓ Branch 3 → 4 taken 16 times.
✓ Branch 3 → 6 taken 2 times.
18 if (owner) owner->finishAsync(id, attempt, succeeded);
73 18 });
74 16 return true;
75 16 }
76
77
1/1
✓ Branch 39 → 40 taken 25 times.
25 UndoEntry entry = m_stack.takeLast();
78
1/1
✓ Branch 40 → 41 taken 25 times.
25 const QPointer<UndoManager> owner(this);
79
6/6
✓ Branch 41 → 42 taken 25 times.
✓ Branch 45 → 46 taken 25 times.
✓ Branch 46 → 47 taken 25 times.
✓ Branch 47 → 48 taken 25 times.
✓ Branch 55 → 43 taken 25 times.
✓ Branch 55 → 56 taken 25 times.
50 qCInfo(lcUndo) << "Undo:" << entry.description
80
3/3
✓ Branch 48 → 49 taken 25 times.
✓ Branch 50 → 51 taken 25 times.
✓ Branch 51 → 52 taken 25 times.
25 << "(stack size:" << m_stack.size() << ")";
81
82
1/2
✓ Branch 57 → 58 taken 25 times.
✗ Branch 57 → 59 not taken.
25 if (entry.undo) {
83
1/1
✓ Branch 58 → 59 taken 25 times.
25 entry.undo();
84 }
85
86
2/3
✓ Branch 60 → 61 taken 25 times.
✗ Branch 60 → 62 not taken.
✓ Branch 61 → 62 taken 25 times.
25 if (owner) emit undoPerformed(entry.description);
87
2/3
✓ Branch 63 → 64 taken 25 times.
✗ Branch 63 → 66 not taken.
✓ Branch 65 → 66 taken 25 times.
25 if (owner) emit undoAvailable(canUndo());
88 25 return true;
89 25 }
90
91 16 void UndoManager::finishAsync(quint64 id, quint64 attempt, bool succeeded) {
92
4/4
✓ Branch 2 → 3 taken 14 times.
✓ Branch 2 → 4 taken 2 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 13 times.
16 if (m_activeId != id || m_activeAttempt != attempt) return;
93
1/1
✓ Branch 7 → 8 taken 13 times.
13 const auto entry = std::find_if(m_stack.cbegin(), m_stack.cend(),
94 13 [id](const UndoEntry &value) { return value.id == id; });
95
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 13 times.
13 if (entry == m_stack.cend()) return;
96 13 const QString description = entry->description;
97
3/3
✓ Branch 14 → 15 taken 8 times.
✓ Branch 14 → 23 taken 5 times.
✓ Branch 22 → 23 taken 8 times.
21 if (succeeded) m_stack.removeAt(std::distance(m_stack.cbegin(), entry));
98 13 m_activeId = 0;
99
1/1
✓ Branch 23 → 24 taken 13 times.
13 const QPointer<UndoManager> owner(this);
100
3/3
✓ Branch 24 → 25 taken 8 times.
✓ Branch 24 → 26 taken 5 times.
✓ Branch 25 → 27 taken 8 times.
13 if (succeeded) emit undoPerformed(description);
101
1/1
✓ Branch 26 → 27 taken 5 times.
5 else emit undoFailed(description);
102
2/3
✓ Branch 28 → 29 taken 13 times.
✗ Branch 28 → 31 not taken.
✓ Branch 30 → 31 taken 13 times.
13 if (owner) emit undoAvailable(canUndo());
103 13 }
104
105
4/4
✓ Branch 3 → 4 taken 468 times.
✓ Branch 3 → 7 taken 62 times.
✓ Branch 5 → 6 taken 431 times.
✓ Branch 5 → 7 taken 37 times.
530 bool UndoManager::canUndo() const { return !busy() && !m_stack.isEmpty(); }
106
107 7 QString UndoManager::lastDescription() const {
108
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 6 times.
7 if (m_stack.isEmpty())
109 1 return {};
110 6 return m_stack.last().description;
111 }
112
113 59 int UndoManager::stackSize() const { return m_stack.size(); }
114
115 1910 void UndoManager::clear() {
116
2/2
✓ Branch 3 → 4 taken 1809 times.
✓ Branch 3 → 5 taken 101 times.
1910 if (m_stack.isEmpty())
117 1809 return;
118 101 m_stack.clear();
119 101 m_activeId = 0;
120
5/5
✓ Branch 6 → 7 taken 101 times.
✓ Branch 10 → 11 taken 101 times.
✓ Branch 11 → 12 taken 101 times.
✓ Branch 15 → 8 taken 101 times.
✓ Branch 15 → 16 taken 101 times.
202 qCInfo(lcUndo) << "Stack cleared";
121 101 emit undoAvailable(false);
122 }
123