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: 91.2% 177 / 0 / 194
Functions: 100.0% 18 / 0 / 18
Branches: 85.2% 213 / 0 / 250

presentation/WorkspacePersistence.cpp
Line Branch Exec Source
1 #include "WorkspacePersistence.h"
2 #include "QuickTrayController.h"
3
4 776 WorkspacePersistence::WorkspacePersistence(QString directory, WorkspaceState &workspace,
5 776 DesignTokens &tokens, QObject *parent)
6
5/5
✓ Branch 5 → 6 taken 776 times.
✓ Branch 7 → 8 taken 776 times.
✓ Branch 13 → 14 taken 776 times.
✓ Branch 15 → 16 taken 776 times.
✓ Branch 16 → 17 taken 776 times.
776 : QObject(parent), m_store(std::move(directory)), m_workspace(workspace), m_tokens(tokens) {
7
1/1
✓ Branch 17 → 18 taken 776 times.
776 m_timer.setSingleShot(true);
8
1/1
✓ Branch 18 → 19 taken 776 times.
776 m_timer.setInterval(200);
9
1/1
✓ Branch 19 → 20 taken 776 times.
1349 connect(&m_timer, &QTimer::timeout, this, [this] { flushAsync(); });
10
1/1
✓ Branch 21 → 22 taken 776 times.
776 connect(&workspace, &WorkspaceState::selectionChanged, this, &WorkspacePersistence::schedule);
11
1/1
✓ Branch 23 → 24 taken 776 times.
776 connect(&workspace, &WorkspaceState::readingModeChanged, this, &WorkspacePersistence::schedule);
12
1/1
✓ Branch 25 → 26 taken 776 times.
776 connect(&workspace, &WorkspaceState::areaChanged, this, &WorkspacePersistence::schedule);
13
1/1
✓ Branch 27 → 28 taken 776 times.
776 connect(&workspace, &WorkspaceState::folderExpansionChanged, this, &WorkspacePersistence::schedule);
14
1/1
✓ Branch 29 → 30 taken 776 times.
776 connect(&workspace, &WorkspaceState::readerStateChanged, this, &WorkspacePersistence::schedule);
15
1/1
✓ Branch 31 → 32 taken 776 times.
776 connect(&workspace, &WorkspaceState::layoutChanged, this, &WorkspacePersistence::schedule);
16
1/1
✓ Branch 33 → 34 taken 776 times.
776 connect(&workspace, &QAbstractItemModel::rowsRemoved, this, &WorkspacePersistence::schedule);
17
1/1
✓ Branch 35 → 36 taken 776 times.
776 connect(&workspace, &QAbstractItemModel::modelReset, this, &WorkspacePersistence::schedule);
18
1/1
✓ Branch 37 → 38 taken 776 times.
776 connect(&tokens, &DesignTokens::changed, this, &WorkspacePersistence::schedule);
19 // P90.17: the writer owns the only writing UiStateStore on its own
20 // thread; the GUI thread only snapshots immutable state.
21
3/6
✓ Branch 39 → 40 taken 776 times.
✓ Branch 42 → 43 taken 776 times.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 776 times.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 83 not taken.
776 auto *writer = new UiStateWriter(m_store.directory());
22
1/1
✓ Branch 46 → 47 taken 776 times.
776 writer->moveToThread(&m_writerThread);
23
1/1
✓ Branch 47 → 48 taken 776 times.
776 connect(&m_writerThread, &QThread::finished, writer, &QObject::deleteLater);
24
1/1
✓ Branch 49 → 50 taken 776 times.
776 m_writer = writer;
25
1/1
✓ Branch 50 → 51 taken 776 times.
776 m_writerThread.start();
26 // The writing store must pass the same load gate (writability, corrupt
27 // session refusal) as the GUI-side store before the first write.
28
1/1
✓ Branch 51 → 52 taken 776 times.
776 QMetaObject::invokeMethod(writer, &UiStateWriter::initialize, Qt::BlockingQueuedConnection);
29 776 connect(writer, &UiStateWriter::written, this,
30
1/1
✓ Branch 52 → 53 taken 776 times.
776 [this](quint64 generation, bool ok, const QString &error) {
31
2/2
✓ Branch 2 → 3 taken 22 times.
✓ Branch 2 → 4 taken 481 times.
503 if (generation != m_writeGeneration) return; // superseded result
32 481 m_writeInFlight = false;
33
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 12 taken 481 times.
481 if (!ok) {
34 ✗ m_error = error;
35 // The dispatched snapshot is only an optimization after
36 // a successful commit. A rejected write must stay dirty
37 // so a later flush can retry the same state.
38 ✗ m_lastDispatched = {};
39 ✗ if (!m_pendingWrite) m_dirty = true;
40 ✗ emit saveFailed(error);
41 } else {
42 481 m_error.clear();
43
3/4
✓ Branch 13 → 14 taken 58 times.
✓ Branch 13 → 19 taken 423 times.
✓ Branch 14 → 15 taken 58 times.
✗ Branch 14 → 19 not taken.
539 if (m_dirty && !m_pendingWrite &&
44
8/10
✓ Branch 15 → 16 taken 58 times.
✓ Branch 16 → 17 taken 58 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 56 times.
✓ Branch 20 → 21 taken 58 times.
✓ Branch 20 → 22 taken 423 times.
✓ Branch 22 → 23 taken 2 times.
✓ Branch 22 → 24 taken 479 times.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
539 buildSnapshot() == m_lastDispatched)
45 2 m_dirty = false;
46 481 emit saved();
47 }
48 481 dispatchPendingWrite();
49 ✗ });
50 776 }
51
52 1541 WorkspacePersistence::~WorkspacePersistence() {
53 776 m_writerThread.quit();
54 // Destroying the QThread while its writer runs aborts the process; the
55 // former 2 s limit was exceeded on a loaded CI runner.
56
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 12 taken 776 times.
776 if (!m_writerThread.wait(5000)) {
57 ✗ qWarning() << "Workspace writer still busy after 5 s; waiting for it";
58 ✗ m_writerThread.wait();
59 }
60 1541 }
61
62 777 bool WorkspacePersistence::load(const QString &legacyIniPath,
63 const std::function<bool(const MailId &)> &acceptIdentity) {
64
1/1
✓ Branch 2 → 3 taken 777 times.
777 m_timer.stop();
65 777 m_loaded = false;
66 777 m_dirty = false;
67 777 m_pendingWrite = false;
68 777 m_error.clear();
69
1/1
✓ Branch 4 → 5 taken 777 times.
777 QJsonObject session;
70
7/8
✓ Branch 5 → 6 taken 777 times.
✓ Branch 6 → 7 taken 777 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 777 times.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 10 taken 776 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 16 taken 776 times.
777 if (!m_store.load(legacyIniPath) || !m_store.readSession(&session)) {
71 1 m_error = m_store.error();
72 1 return false;
73 }
74
6/8
✓ Branch 16 → 17 taken 776 times.
✓ Branch 17 → 18 taken 13 times.
✓ Branch 17 → 21 taken 763 times.
✓ Branch 18 → 19 taken 13 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 13 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 776 times.
776 if (!session.isEmpty() && !m_workspace.restoreSession(session, &m_error, acceptIdentity)) return false;
75 776 const auto preferences = m_store.preferences();
76
4/4
✓ Branch 26 → 27 taken 776 times.
✓ Branch 27 → 28 taken 776 times.
✓ Branch 28 → 29 taken 776 times.
✓ Branch 29 → 30 taken 776 times.
776 m_workspace.setLayout(preferences.value("ui/layout").toString());
77 // S106-332: the default reading mode is a QSettings value that the
78 // workspace reads itself; the copy in ui-preferences.json (kept from the
79 // migration, never refreshed) is not applied over it.
80
4/4
✓ Branch 35 → 36 taken 776 times.
✓ Branch 36 → 37 taken 776 times.
✓ Branch 37 → 38 taken 776 times.
✓ Branch 38 → 39 taken 776 times.
776 m_tokens.setTheme(preferences.value("appearance/theme").toString());
81
4/4
✓ Branch 44 → 45 taken 776 times.
✓ Branch 45 → 46 taken 776 times.
✓ Branch 46 → 47 taken 776 times.
✓ Branch 47 → 48 taken 776 times.
776 m_tokens.setAccentColor(preferences.value("appearance/accentColor").toString());
82
4/4
✓ Branch 53 → 54 taken 776 times.
✓ Branch 54 → 55 taken 776 times.
✓ Branch 55 → 56 taken 776 times.
✓ Branch 56 → 57 taken 776 times.
776 m_tokens.setFontScale(preferences.value("ui/fontScale").toDouble());
83 // B3: the stored policy governs; "system" evaluates against the live
84 // desktop preference at restore time.
85
4/4
✓ Branch 61 → 62 taken 776 times.
✓ Branch 62 → 63 taken 776 times.
✓ Branch 63 → 64 taken 776 times.
✓ Branch 64 → 65 taken 776 times.
776 m_tokens.applyMotionPolicy(preferences.value("ui/motion").toString());
86
3/3
✓ Branch 70 → 71 taken 776 times.
✓ Branch 71 → 72 taken 776 times.
✓ Branch 72 → 73 taken 776 times.
776 m_threaded = preferences.value("view/threaded").toBool();
87
3/3
✓ Branch 77 → 78 taken 776 times.
✓ Branch 78 → 79 taken 776 times.
✓ Branch 79 → 80 taken 776 times.
776 m_suggestions = preferences.value("view/suggestions").toBool();
88
3/3
✓ Branch 84 → 85 taken 776 times.
✓ Branch 85 → 86 taken 776 times.
✓ Branch 86 → 87 taken 776 times.
776 m_composeWindowed = preferences.value("ui/composeWindowed").toBool();
89
3/3
✓ Branch 91 → 92 taken 776 times.
✓ Branch 92 → 93 taken 776 times.
✓ Branch 93 → 94 taken 776 times.
776 m_closeToTray = preferences.value("ui/closeToTray", true).toBool();
90 776 for (const auto *key : {"viewMode", "workWeek", "hourZoom", "hiddenCalendars", "lastCalendar", "agendaPanel", "agendaWidth", "agendaTasksHeight",
91 "agendaTasks", "agendaTasksShown", "agendaTaskFilter", "agendaTaskSort",
92 "workdayStart", "workdayEnd", "workdays", "firstDayOfWeek", "tasks", "lastTaskList",
93
2/2
✓ Branch 119 → 100 taken 14744 times.
✓ Branch 119 → 120 taken 776 times.
16296 "taskDefaultReminder"})
94
2/2
✓ Branch 109 → 110 taken 14744 times.
✓ Branch 110 → 111 taken 14744 times.
14744 m_calendarState.insert(QString::fromLatin1(key),
95
3/3
✓ Branch 102 → 103 taken 14744 times.
✓ Branch 106 → 107 taken 14744 times.
✓ Branch 107 → 108 taken 14744 times.
44232 preferences.value(QStringLiteral("calendar/") + QString::fromLatin1(key)));
96
2/2
✓ Branch 124 → 125 taken 776 times.
✓ Branch 125 → 126 taken 776 times.
1552 m_commandBarState = {{QStringLiteral("history"), preferences.value("commandbar/history")},
97
4/6
✓ Branch 131 → 132 taken 776 times.
✓ Branch 132 → 133 taken 776 times.
✓ Branch 138 → 139 taken 1552 times.
✓ Branch 138 → 140 taken 776 times.
✗ Branch 317 → 318 not taken.
✗ Branch 317 → 319 not taken.
3104 {QStringLiteral("recentFolders"), preferences.value("commandbar/recentFolders")}};
98
3/3
✓ Branch 154 → 155 taken 776 times.
✓ Branch 155 → 156 taken 776 times.
✓ Branch 156 → 157 taken 776 times.
1552 m_searchState = {{QStringLiteral("timelineCollapsed"), preferences.value("search/timelineCollapsed").toBool()},
99
3/3
✓ Branch 162 → 163 taken 776 times.
✓ Branch 163 → 164 taken 776 times.
✓ Branch 164 → 165 taken 776 times.
1552 {QStringLiteral("timelineHintSeen"), preferences.value("search/timelineHintSeen").toBool()},
100
5/7
✓ Branch 170 → 171 taken 776 times.
✓ Branch 171 → 172 taken 776 times.
✓ Branch 172 → 173 taken 776 times.
✓ Branch 178 → 179 taken 2328 times.
✓ Branch 178 → 180 taken 776 times.
✗ Branch 358 → 359 not taken.
✗ Branch 358 → 360 not taken.
3880 {QStringLiteral("grouped"), preferences.value("search/grouped").toBool()}};
101 776 m_loaded = true;
102 776 return true;
103 6209 }
104
105 340 void WorkspacePersistence::setThreaded(bool threaded) {
106
2/2
✓ Branch 2 → 3 taken 291 times.
✓ Branch 2 → 4 taken 49 times.
340 if (m_threaded == threaded) return;
107 49 m_threaded = threaded;
108 49 schedule();
109 }
110
111 23 void WorkspacePersistence::setSuggestions(bool on) {
112
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 23 times.
23 if (m_suggestions == on) return;
113 23 m_suggestions = on;
114 23 schedule();
115 }
116
117 9 void WorkspacePersistence::setComposeWindowed(bool windowed) {
118
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 9 times.
9 if (m_composeWindowed == windowed) return;
119 9 m_composeWindowed = windowed;
120 9 schedule();
121 }
122
123 765 void WorkspacePersistence::restoreTray(QuickTrayController *tray) {
124 765 m_tray = tray;
125
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 765 times.
765 if (!tray) return;
126
4/4
✓ Branch 7 → 8 taken 765 times.
✓ Branch 8 → 9 taken 765 times.
✓ Branch 9 → 10 taken 765 times.
✓ Branch 10 → 11 taken 765 times.
765 tray->setCloseToTray(m_store.preferences().value("ui/closeToTray", true).toBool());
127 }
128
129 8 void WorkspacePersistence::setCloseToTray(bool on) {
130
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 8 times.
8 if (m_closeToTray == on) return;
131 8 m_closeToTray = on;
132
1/2
✓ Branch 5 → 6 taken 8 times.
✗ Branch 5 → 8 not taken.
8 if (m_tray) m_tray->setCloseToTray(on);
133 8 schedule();
134 }
135
136 307 void WorkspacePersistence::setCalendarState(const QVariantMap &state) {
137
2/2
✓ Branch 3 → 4 taken 15 times.
✓ Branch 3 → 5 taken 292 times.
307 if (m_calendarState == state) return;
138 292 m_calendarState = state;
139 292 schedule();
140 }
141
142 262 void WorkspacePersistence::setCommandBarState(const QVariantMap &state) {
143
2/2
✓ Branch 3 → 4 taken 6 times.
✓ Branch 3 → 5 taken 256 times.
262 if (m_commandBarState == state) return;
144 256 m_commandBarState = state;
145 256 schedule();
146 }
147
148 13 void WorkspacePersistence::setSearchState(const QVariantMap &state) {
149
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 13 times.
13 if (m_searchState == state) return;
150 13 m_searchState = state;
151 13 schedule();
152 }
153
154 4498 void WorkspacePersistence::schedule() {
155
2/2
✓ Branch 2 → 3 taken 849 times.
✓ Branch 2 → 4 taken 3649 times.
4498 if (!m_loaded) return;
156 3649 m_dirty = true;
157 3649 m_timer.start();
158 }
159
160 676 WorkspacePersistence::Snapshot WorkspacePersistence::buildSnapshot() const {
161
1/1
✓ Branch 2 → 3 taken 676 times.
676 Snapshot snapshot;
162
1/1
✓ Branch 3 → 4 taken 676 times.
676 snapshot.session = m_workspace.saveSession();
163 676 auto preferences = m_store.preferences();
164
2/2
✓ Branch 9 → 10 taken 676 times.
✓ Branch 10 → 11 taken 676 times.
676 preferences["ui/layout"] = m_workspace.layout();
165
2/2
✓ Branch 17 → 18 taken 676 times.
✓ Branch 18 → 19 taken 676 times.
676 preferences["appearance/theme"] = m_tokens.theme();
166
2/2
✓ Branch 25 → 26 taken 676 times.
✓ Branch 26 → 27 taken 676 times.
676 preferences["appearance/accentColor"] = m_tokens.accentColor();
167
2/2
✓ Branch 33 → 34 taken 676 times.
✓ Branch 34 → 35 taken 676 times.
676 preferences["ui/fontScale"] = m_tokens.fontScale();
168
2/2
✓ Branch 40 → 41 taken 676 times.
✓ Branch 41 → 42 taken 676 times.
676 preferences["ui/motion"] = m_tokens.motionPolicy();
169
2/2
✓ Branch 47 → 48 taken 676 times.
✓ Branch 48 → 49 taken 676 times.
676 preferences["view/threaded"] = m_threaded;
170
2/2
✓ Branch 53 → 54 taken 676 times.
✓ Branch 54 → 55 taken 676 times.
676 preferences["view/suggestions"] = m_suggestions;
171
2/2
✓ Branch 59 → 60 taken 676 times.
✓ Branch 60 → 61 taken 676 times.
676 preferences["ui/composeWindowed"] = m_composeWindowed;
172
2/2
✓ Branch 65 → 66 taken 676 times.
✓ Branch 66 → 67 taken 676 times.
676 preferences["ui/closeToTray"] = m_closeToTray;
173
4/4
✓ Branch 70 → 71 taken 676 times.
✓ Branch 84 → 85 taken 13520 times.
✓ Branch 86 → 72 taken 12844 times.
✓ Branch 86 → 87 taken 676 times.
13520 for (auto it = m_calendarState.cbegin(); it != m_calendarState.cend(); ++it)
174
3/3
✓ Branch 77 → 78 taken 12844 times.
✓ Branch 78 → 79 taken 12844 times.
✓ Branch 79 → 80 taken 12844 times.
25688 preferences[QStringLiteral("calendar/") + it.key()] = it.value();
175
4/4
✓ Branch 91 → 92 taken 676 times.
✓ Branch 92 → 93 taken 676 times.
✓ Branch 94 → 95 taken 676 times.
✓ Branch 95 → 96 taken 676 times.
1352 preferences["commandbar/history"] = m_commandBarState.value(QStringLiteral("history")).toStringList();
176
4/4
✓ Branch 108 → 109 taken 676 times.
✓ Branch 109 → 110 taken 676 times.
✓ Branch 111 → 112 taken 676 times.
✓ Branch 112 → 113 taken 676 times.
1352 preferences["commandbar/recentFolders"] = m_commandBarState.value(QStringLiteral("recentFolders")).toMap();
177
4/4
✓ Branch 125 → 126 taken 676 times.
✓ Branch 126 → 127 taken 676 times.
✓ Branch 128 → 129 taken 676 times.
✓ Branch 129 → 130 taken 676 times.
1352 preferences["search/timelineCollapsed"] = m_searchState.value(QStringLiteral("timelineCollapsed")).toBool();
178
4/4
✓ Branch 141 → 142 taken 676 times.
✓ Branch 142 → 143 taken 676 times.
✓ Branch 144 → 145 taken 676 times.
✓ Branch 145 → 146 taken 676 times.
1352 preferences["search/timelineHintSeen"] = m_searchState.value(QStringLiteral("timelineHintSeen")).toBool();
179
4/4
✓ Branch 157 → 158 taken 676 times.
✓ Branch 158 → 159 taken 676 times.
✓ Branch 160 → 161 taken 676 times.
✓ Branch 161 → 162 taken 676 times.
1352 preferences["search/grouped"] = m_searchState.value(QStringLiteral("grouped")).toBool();
180 676 snapshot.preferences = preferences;
181 676 return snapshot;
182 676 }
183
184 573 void WorkspacePersistence::flushAsync() {
185 573 m_timer.stop();
186
3/4
✓ Branch 3 → 4 taken 573 times.
✗ Branch 3 → 5 not taken.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 6 taken 571 times.
573 if (!m_loaded || !m_dirty) return;
187 // The GUI thread only pays for the immutable snapshot; a newer snapshot
188 // replaces an older pending one (scroll/selection storms coalesce).
189
1/1
✓ Branch 6 → 7 taken 571 times.
571 m_pendingSnapshot = buildSnapshot();
190 571 m_pendingWrite = true;
191 571 m_dirty = false;
192
2/2
✓ Branch 10 → 11 taken 9 times.
✓ Branch 10 → 12 taken 562 times.
571 if (m_pendingSnapshot == m_lastDispatched) {
193 // The same snapshot was committed or is currently in flight. Its
194 // failure path restores dirty and invalidates m_lastDispatched.
195 9 m_pendingWrite = false;
196 9 return;
197 }
198 562 dispatchPendingWrite();
199 }
200
201 1043 void WorkspacePersistence::dispatchPendingWrite() {
202
7/8
✓ Branch 2 → 3 taken 563 times.
✓ Branch 2 → 6 taken 480 times.
✓ Branch 3 → 4 taken 560 times.
✓ Branch 3 → 6 taken 3 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 560 times.
✓ Branch 8 → 9 taken 483 times.
✓ Branch 8 → 10 taken 560 times.
1043 if (!m_pendingWrite || m_writeInFlight || !m_writer) return;
203 560 m_lastDispatched = m_pendingSnapshot;
204 560 m_pendingWrite = false;
205 560 m_writeInFlight = true;
206 560 ++m_writeGeneration;
207
1/1
✓ Branch 15 → 16 taken 560 times.
560 QMetaObject::invokeMethod(m_writer, "write", Qt::QueuedConnection,
208
1/1
✓ Branch 13 → 14 taken 560 times.
560 Q_ARG(quint64, m_writeGeneration),
209
1/1
✓ Branch 12 → 13 taken 560 times.
560 Q_ARG(QJsonObject, m_lastDispatched.session),
210
1/1
✓ Branch 11 → 12 taken 560 times.
1120 Q_ARG(QVariantMap, m_lastDispatched.preferences));
211 }
212
213 50 bool WorkspacePersistence::flush() {
214 50 m_timer.stop();
215
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 49 times.
50 if (!m_loaded) return false;
216
4/6
✓ Branch 5 → 6 taken 2 times.
✓ Branch 5 → 9 taken 47 times.
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 9 not taken.
49 if (!m_dirty && !m_pendingWrite && !m_writeInFlight) return true;
217 // Shutdown contract: durable before returning. Newest state wins; the
218 // blocking call serializes behind any in-flight write on the writer
219 // thread (the writer never calls back into the GUI thread).
220
1/1
✓ Branch 9 → 10 taken 47 times.
47 m_pendingSnapshot = buildSnapshot();
221 47 m_pendingWrite = false;
222 47 m_dirty = false;
223
3/6
✓ Branch 13 → 14 taken 47 times.
✗ Branch 13 → 17 not taken.
✓ Branch 15 → 16 taken 47 times.
✗ Branch 15 → 17 not taken.
✓ Branch 18 → 19 taken 47 times.
✗ Branch 18 → 38 not taken.
47 if (m_writer && m_writerThread.isRunning()) {
224 47 bool ok = false;
225 47 ++m_writeGeneration;
226
2/2
✓ Branch 22 → 23 taken 47 times.
✓ Branch 24 → 25 taken 47 times.
47 QMetaObject::invokeMethod(m_writer, "write", Qt::BlockingQueuedConnection,
227 Q_RETURN_ARG(bool, ok),
228
1/1
✓ Branch 21 → 22 taken 47 times.
47 Q_ARG(quint64, m_writeGeneration),
229
1/1
✓ Branch 20 → 21 taken 47 times.
47 Q_ARG(QJsonObject, m_pendingSnapshot.session),
230
1/1
✓ Branch 19 → 20 taken 47 times.
47 Q_ARG(QVariantMap, m_pendingSnapshot.preferences));
231 47 m_lastDispatched = m_pendingSnapshot;
232 47 m_writeInFlight = false;
233
2/2
✓ Branch 26 → 27 taken 1 time.
✓ Branch 26 → 35 taken 46 times.
47 if (!ok) {
234 1 m_lastDispatched = {};
235 1 m_dirty = true;
236
1/1
✓ Branch 30 → 31 taken 1 time.
1 m_error = tr("Workspace state could not be saved.");
237
1/1
✓ Branch 33 → 34 taken 1 time.
1 emit saveFailed(m_error);
238 1 return false;
239 }
240 46 m_error.clear();
241 46 return true;
242 }
243 // Fallback (writer thread already stopped): synchronous GUI write.
244 ✗ if (!m_store.saveSession(m_pendingSnapshot.session) ||
245 ✗ !m_store.savePreferences(m_pendingSnapshot.preferences)) {
246 ✗ m_lastDispatched = {};
247 ✗ m_dirty = true;
248 ✗ m_error = m_store.error();
249 ✗ emit saveFailed(m_error);
250 ✗ return false;
251 }
252 ✗ m_lastDispatched = m_pendingSnapshot;
253 ✗ m_error.clear();
254 ✗ return true;
255 1 }
256