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: 90.1% 146 / 0 / 162
Functions: 80.8% 21 / 0 / 26
Branches: 85.1% 160 / 0 / 188

app/core/CalendarSyncStatus.cpp
Line Branch Exec Source
1 #include "CalendarSyncStatus.h"
2
3 #include <QDateTime>
4 #include <QLoggingCategory>
5 #include <QSettings>
6
7
4/7
✓ Branch 2 → 3 taken 6 times.
✓ Branch 2 → 8 taken 275 times.
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
281 Q_LOGGING_CATEGORY(lcCalSyncStatus, "mailjd.calendar.syncstatus")
8
9 namespace {
10 2033 QString groupFor(const QString &accountId) {
11 // QSettings keys must not contain '/' from the id; ids are UUIDs, but
12 // keep the key well-formed for any configured value.
13 2033 QString key = accountId;
14
1/1
✓ Branch 7 → 8 taken 2033 times.
2033 key.replace(QLatin1Char('/'), QLatin1Char('_'));
15
1/1
✓ Branch 11 → 12 taken 2033 times.
4066 return QStringLiteral("caldav/status/") + key;
16 2033 }
17 } // namespace
18
19 950 CalendarSyncStatus::CalendarSyncStatus(QObject *parent) : QObject(parent) {}
20
21 12 QString CalendarSyncStatus::kindForNetworkError(QNetworkReply::NetworkError error) {
22
5/5
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 8 taken 2 times.
✓ Branch 2 → 13 taken 1 time.
✓ Branch 2 → 18 taken 1 time.
✓ Branch 2 → 23 taken 4 times.
12 switch (error) {
23 4 case QNetworkReply::AuthenticationRequiredError:
24 case QNetworkReply::ContentAccessDenied:
25 4 return QStringLiteral("authentication");
26 2 case QNetworkReply::ConnectionRefusedError:
27 case QNetworkReply::RemoteHostClosedError:
28 case QNetworkReply::HostNotFoundError:
29 case QNetworkReply::TemporaryNetworkFailureError:
30 case QNetworkReply::NetworkSessionFailedError:
31 case QNetworkReply::BackgroundRequestNotAllowedError:
32 case QNetworkReply::UnknownNetworkError:
33 case QNetworkReply::ProxyConnectionRefusedError:
34 case QNetworkReply::ProxyConnectionClosedError:
35 case QNetworkReply::ProxyNotFoundError:
36 case QNetworkReply::ProxyTimeoutError:
37 case QNetworkReply::ProxyAuthenticationRequiredError:
38 case QNetworkReply::UnknownProxyError:
39 2 return QStringLiteral("network");
40 1 case QNetworkReply::SslHandshakeFailedError:
41 1 return QStringLiteral("tls");
42 1 case QNetworkReply::TimeoutError:
43 case QNetworkReply::OperationCanceledError:
44 1 return QStringLiteral("timeout");
45 4 default:
46 4 return QStringLiteral("server");
47 }
48 }
49
50 110 QString CalendarSyncStatus::messageForKind(const QString &kind) {
51
2/2
✓ Branch 4 → 5 taken 50 times.
✓ Branch 4 → 6 taken 60 times.
110 if (kind == QLatin1String("password"))
52 50 return tr("No password is stored for this account. Enter it in Settings → DAV & Sync.");
53
2/2
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 58 times.
60 if (kind == QLatin1String("authorization"))
54 2 return tr("This account needs to be authorized on this device. Enter its password in Settings → DAV & Sync.");
55
2/2
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 14 taken 54 times.
58 if (kind == QLatin1String("authentication"))
56 4 return tr("The server rejected the user name or password. Check them in Settings → DAV & Sync.");
57
2/2
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 18 taken 52 times.
54 if (kind == QLatin1String("network"))
58 2 return tr("The server could not be reached. Check the server address and your connection.");
59
2/2
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 51 times.
52 if (kind == QLatin1String("tls"))
60 1 return tr("The secure connection to the server failed. Check the server address and its certificate.");
61
2/2
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 26 taken 50 times.
51 if (kind == QLatin1String("timeout"))
62 1 return tr("The server did not answer in time. MailJD tries again at the next synchronization.");
63
2/2
✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 30 taken 49 times.
50 if (kind == QLatin1String("local"))
64 1 return tr("The calendar data could not be saved on this computer. MailJD tries again at the next synchronization.");
65
2/2
✓ Branch 31 → 32 taken 43 times.
✓ Branch 31 → 33 taken 6 times.
49 if (kind.isEmpty())
66 43 return {};
67 6 return tr("The server answered with an error. MailJD tries again at the next synchronization.");
68 }
69
70 256 qint64 CalendarSyncStatus::now() const {
71
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 255 times.
256 return m_clock ? m_clock() : QDateTime::currentSecsSinceEpoch();
72 }
73
74 124 void CalendarSyncStatus::store(const CalendarAccountSyncState &state) const {
75
1/1
✓ Branch 2 → 3 taken 124 times.
124 QSettings settings;
76
2/2
✓ Branch 3 → 4 taken 124 times.
✓ Branch 5 → 6 taken 124 times.
124 settings.beginGroup(groupFor(state.accountId));
77
1/1
✓ Branch 12 → 13 taken 124 times.
248 settings.setValue(QStringLiteral("lastAttempt"), state.lastAttempt);
78
1/1
✓ Branch 21 → 22 taken 124 times.
248 settings.setValue(QStringLiteral("lastSuccess"), state.lastSuccess);
79
1/1
✓ Branch 30 → 31 taken 124 times.
248 settings.setValue(QStringLiteral("errorKind"), state.errorKind);
80
2/2
✓ Branch 34 → 35 taken 124 times.
✓ Branch 40 → 41 taken 124 times.
248 settings.setValue(QStringLiteral("errorDetail"), state.errorDetail.left(1024));
81
1/1
✓ Branch 45 → 46 taken 124 times.
124 settings.endGroup();
82 124 }
83
84 144 void CalendarSyncStatus::started(const QString &accountId) {
85
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 144 times.
144 if (accountId.isEmpty())
86 ✗ return;
87
1/1
✓ Branch 5 → 6 taken 144 times.
144 m_running.removeIf([&](const auto &entry) { return entry.accountId == accountId; });
88
1/1
✓ Branch 6 → 7 taken 144 times.
144 CalendarAccountSyncState current = state(accountId);
89 // lastSuccess stays; the error is decided again by this run.
90 144 current.state = QStringLiteral("running");
91
1/1
✓ Branch 13 → 14 taken 144 times.
144 current.lastAttempt = now();
92 144 current.errorKind.clear();
93 144 current.errorDetail.clear();
94
1/1
✓ Branch 16 → 17 taken 144 times.
144 m_running.append(current);
95
6/6
✓ Branch 17 → 18 taken 144 times.
✓ Branch 21 → 22 taken 144 times.
✓ Branch 22 → 23 taken 144 times.
✓ Branch 23 → 24 taken 144 times.
✓ Branch 27 → 19 taken 144 times.
✓ Branch 27 → 28 taken 144 times.
288 qCInfo(lcCalSyncStatus) << "Calendar sync started for account" << accountId;
96
1/1
✓ Branch 28 → 29 taken 144 times.
144 emit changed();
97 144 }
98
99 13 void CalendarSyncStatus::failed(const QString &accountId, const QString &kind,
100 const QString &detail) {
101
3/4
✓ Branch 2 → 3 taken 13 times.
✓ Branch 3 → 4 taken 13 times.
✓ Branch 37 → 5 taken 13 times.
✗ Branch 37 → 38 not taken.
13 for (auto &entry : m_running) {
102
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 13 times.
13 if (entry.accountId != accountId)
103 ✗ continue;
104
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 12 times.
13 if (!entry.errorKind.isEmpty())
105 13 return; // the first error of a run is the cause
106 12 entry.errorKind = kind;
107
1/1
✓ Branch 13 → 14 taken 12 times.
12 entry.errorDetail = detail.left(1024);
108
6/6
✓ Branch 16 → 17 taken 12 times.
✓ Branch 20 → 21 taken 12 times.
✓ Branch 21 → 22 taken 12 times.
✓ Branch 22 → 23 taken 12 times.
✓ Branch 32 → 18 taken 12 times.
✓ Branch 32 → 33 taken 12 times.
24 qCWarning(lcCalSyncStatus) << "Calendar sync failed for account" << accountId
109
5/5
✓ Branch 23 → 24 taken 12 times.
✓ Branch 24 → 25 taken 12 times.
✓ Branch 25 → 26 taken 12 times.
✓ Branch 26 → 27 taken 12 times.
✓ Branch 27 → 28 taken 12 times.
12 << "kind" << kind << "detail" << detail.left(200);
110
1/1
✓ Branch 33 → 34 taken 12 times.
12 emit changed();
111 12 return;
112 }
113 // A failure outside a run (credential lookup before start) still counts.
114 ✗ CalendarAccountSyncState current = state(accountId);
115 ✗ current.state = QStringLiteral("error");
116 ✗ current.lastAttempt = now();
117 ✗ current.errorKind = kind;
118 ✗ current.errorDetail = detail.left(1024);
119 ✗ store(current);
120 ✗ qCWarning(lcCalSyncStatus) << "Calendar sync failed for account" << accountId << "kind" << kind;
121 ✗ emit changed();
122 ✗ }
123
124 124 void CalendarSyncStatus::finished(const QString &accountId) {
125
1/2
✓ Branch 44 → 3 taken 124 times.
✗ Branch 44 → 45 not taken.
124 for (qsizetype i = 0; i < m_running.size(); ++i) {
126
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 124 times.
124 if (m_running.at(i).accountId != accountId)
127 ✗ continue;
128
1/1
✓ Branch 7 → 8 taken 124 times.
124 CalendarAccountSyncState done = m_running.takeAt(i);
129
2/2
✓ Branch 9 → 10 taken 112 times.
✓ Branch 9 → 18 taken 12 times.
124 if (done.errorKind.isEmpty()) {
130 112 done.state = QStringLiteral("ok");
131
1/1
✓ Branch 16 → 17 taken 112 times.
112 done.lastSuccess = now();
132 } else {
133 12 done.state = QStringLiteral("error");
134 }
135
1/1
✓ Branch 25 → 26 taken 124 times.
124 store(done);
136
6/6
✓ Branch 26 → 27 taken 124 times.
✓ Branch 30 → 31 taken 124 times.
✓ Branch 31 → 32 taken 124 times.
✓ Branch 32 → 33 taken 124 times.
✓ Branch 38 → 28 taken 124 times.
✓ Branch 38 → 39 taken 124 times.
248 qCInfo(lcCalSyncStatus) << "Calendar sync finished for account" << accountId
137
2/2
✓ Branch 33 → 34 taken 124 times.
✓ Branch 34 → 35 taken 124 times.
124 << "state" << done.state;
138
1/1
✓ Branch 39 → 40 taken 124 times.
124 emit changed();
139 124 return;
140 124 }
141 }
142
143 20 void CalendarSyncStatus::cancelled(const QString &accountId) {
144 // The run ends without a verdict: the persisted state of the last
145 // finished run applies again.
146 40 const auto removed = m_running.removeIf([&](const auto &entry) { return entry.accountId == accountId; });
147
1/2
✓ Branch 3 → 4 taken 20 times.
✗ Branch 3 → 5 not taken.
20 if (removed > 0) emit changed();
148 20 }
149
150 103 void CalendarSyncStatus::forgetAllExcept(const QStringList &accountIds) {
151 103 QStringList keep;
152
5/5
✓ Branch 5 → 6 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 15 → 4 taken 1 time.
✓ Branch 15 → 16 taken 103 times.
104 for (const QString &id : accountIds) keep << groupFor(id).section(QLatin1Char('/'), 2);
153
1/1
✓ Branch 16 → 17 taken 103 times.
103 QSettings settings;
154
1/1
✓ Branch 21 → 22 taken 103 times.
103 settings.beginGroup(QStringLiteral("caldav/status"));
155
1/1
✓ Branch 24 → 25 taken 103 times.
103 const QStringList stored = settings.childGroups();
156
1/1
✓ Branch 25 → 26 taken 103 times.
103 settings.endGroup();
157
2/2
✓ Branch 53 → 28 taken 2 times.
✓ Branch 53 → 54 taken 103 times.
105 for (const QString &key : stored)
158
2/2
✓ Branch 30 → 31 taken 1 time.
✓ Branch 30 → 51 taken 1 time.
2 if (!keep.contains(key)) {
159
5/5
✓ Branch 31 → 32 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
✓ Branch 36 → 37 taken 1 time.
✓ Branch 40 → 33 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
2 qCInfo(lcCalSyncStatus) << "Forgetting the sync status of a removed account";
160
2/2
✓ Branch 44 → 45 taken 1 time.
✓ Branch 46 → 47 taken 1 time.
1 settings.remove(QStringLiteral("caldav/status/") + key);
161 }
162
1/1
✓ Branch 54 → 55 taken 103 times.
103 m_running.removeIf([&](const auto &entry) { return !accountIds.contains(entry.accountId); });
163
1/1
✓ Branch 55 → 56 taken 103 times.
103 emit changed();
164 103 }
165
166 5 void CalendarSyncStatus::forget(const QString &accountId) {
167
1/1
✓ Branch 2 → 3 taken 5 times.
5 m_running.removeIf([&](const auto &entry) { return entry.accountId == accountId; });
168
1/1
✓ Branch 3 → 4 taken 5 times.
5 QSettings settings;
169
2/2
✓ Branch 4 → 5 taken 5 times.
✓ Branch 6 → 7 taken 5 times.
5 settings.remove(groupFor(accountId));
170
1/1
✓ Branch 8 → 9 taken 5 times.
5 emit changed();
171 5 }
172
173 1904 CalendarAccountSyncState CalendarSyncStatus::state(const QString &accountId) const {
174
2/2
✓ Branch 11 → 4 taken 1 time.
✓ Branch 11 → 12 taken 1903 times.
1904 for (const auto &entry : m_running)
175
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 9 not taken.
1 if (entry.accountId == accountId)
176 1 return entry;
177
1/1
✓ Branch 12 → 13 taken 1903 times.
1903 CalendarAccountSyncState result;
178 1903 result.accountId = accountId;
179
1/1
✓ Branch 14 → 15 taken 1903 times.
1903 QSettings settings;
180
2/2
✓ Branch 15 → 16 taken 1903 times.
✓ Branch 17 → 18 taken 1903 times.
1903 settings.beginGroup(groupFor(accountId));
181
2/2
✓ Branch 24 → 25 taken 1903 times.
✓ Branch 25 → 26 taken 1903 times.
3806 result.lastAttempt = settings.value(QStringLiteral("lastAttempt"), 0).toLongLong();
182
2/2
✓ Branch 35 → 36 taken 1903 times.
✓ Branch 36 → 37 taken 1903 times.
3806 result.lastSuccess = settings.value(QStringLiteral("lastSuccess"), 0).toLongLong();
183
2/2
✓ Branch 45 → 46 taken 1903 times.
✓ Branch 46 → 47 taken 1903 times.
1903 result.errorKind = settings.value(QStringLiteral("errorKind")).toString();
184
2/2
✓ Branch 56 → 57 taken 1903 times.
✓ Branch 57 → 58 taken 1903 times.
1903 result.errorDetail = settings.value(QStringLiteral("errorDetail")).toString();
185
1/1
✓ Branch 63 → 64 taken 1903 times.
1903 settings.endGroup();
186
2/2
✓ Branch 65 → 66 taken 60 times.
✓ Branch 65 → 73 taken 1843 times.
1903 if (!result.errorKind.isEmpty())
187 60 result.state = QStringLiteral("error");
188
2/2
✓ Branch 73 → 74 taken 89 times.
✓ Branch 73 → 81 taken 1754 times.
1843 else if (result.lastSuccess > 0)
189 89 result.state = QStringLiteral("ok");
190 1903 return result;
191 1903 }
192
193 ✗ bool CalendarSyncStatus::running() const { return !m_running.isEmpty(); }
194
195 6 void CalendarSyncStatus::setServerScheduling(const QString &accountId, bool scheduling) {
196
3/3
✓ Branch 2 → 3 taken 6 times.
✓ Branch 7 → 8 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
12 QSettings().setValue(QStringLiteral("caldav/scheduling/") + accountId, scheduling);
197 6 }
198
199 103 bool CalendarSyncStatus::serverScheduling(const QString &accountId) {
200
4/4
✓ Branch 2 → 3 taken 103 times.
✓ Branch 7 → 8 taken 103 times.
✓ Branch 9 → 10 taken 103 times.
✓ Branch 10 → 11 taken 103 times.
206 return QSettings().value(QStringLiteral("caldav/scheduling/") + accountId, false).toBool();
201 }
202
203 4 void CalendarSyncStatus::setScheduling(const QString &accountId, const QString &outboxUrl,
204 const QStringList &addresses) {
205
1/1
✓ Branch 2 → 3 taken 4 times.
4 QSettings settings;
206
3/3
✓ Branch 3 → 4 taken 4 times.
✓ Branch 8 → 9 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
8 settings.setValue(QStringLiteral("caldav/scheduleOutbox/") + accountId, outboxUrl.left(2048));
207
3/3
✓ Branch 16 → 17 taken 4 times.
✓ Branch 21 → 22 taken 4 times.
✓ Branch 23 → 24 taken 4 times.
8 settings.setValue(QStringLiteral("caldav/scheduleAddresses/") + accountId, addresses.mid(0, 50));
208 4 }
209
210 7 QString CalendarSyncStatus::scheduleOutbox(const QString &accountId) {
211
4/4
✓ Branch 2 → 3 taken 7 times.
✓ Branch 6 → 7 taken 7 times.
✓ Branch 8 → 9 taken 7 times.
✓ Branch 9 → 10 taken 7 times.
14 return QSettings().value(QStringLiteral("caldav/scheduleOutbox/") + accountId).toString();
212 }
213
214 67 QStringList CalendarSyncStatus::schedulingAddresses(const QString &accountId) {
215
4/4
✓ Branch 2 → 3 taken 67 times.
✓ Branch 6 → 7 taken 67 times.
✓ Branch 8 → 9 taken 67 times.
✓ Branch 9 → 10 taken 67 times.
134 return QSettings().value(QStringLiteral("caldav/scheduleAddresses/") + accountId).toStringList();
216 }
217
218 ✗ void CalendarSyncStatus::setAutoSchedule(const QString &accountId, bool autoSchedule) {
219 ✗ QSettings().setValue(QStringLiteral("caldav/autoSchedule/") + accountId, autoSchedule);
220 ✗ }
221
222 67 std::optional<bool> CalendarSyncStatus::autoSchedule(const QString &accountId) {
223
3/3
✓ Branch 2 → 3 taken 67 times.
✓ Branch 6 → 7 taken 67 times.
✓ Branch 8 → 9 taken 67 times.
134 const QVariant value = QSettings().value(QStringLiteral("caldav/autoSchedule/") + accountId);
224
2/4
✓ Branch 13 → 14 taken 67 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 17 taken 67 times.
✗ Branch 15 → 16 not taken.
134 return value.isValid() ? std::optional<bool>(value.toBool()) : std::nullopt;
225 67 }
226