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: 75.0% 99 / 0 / 132
Functions: 71.4% 10 / 0 / 14
Branches: 52.7% 144 / 0 / 273

presentation/QuickCalendarSync.cpp
Line Branch Exec Source
1 #include "QuickCalendarSync.h"
2
3 #include "DavAccountSettings.h"
4 #include "app/core/CalendarSubscriptions.h"
5 #include "app/core/CalendarSyncCoordinator.h"
6 #include "app/core/CalendarSyncStatus.h"
7 #include "app/core/ContactSyncCoordinator.h"
8
9 #include <QDateTime>
10 #include <QLocale>
11 #include <QLoggingCategory>
12 #include <QUrl>
13
14
3/7
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 8 not taken.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
1 Q_LOGGING_CATEGORY(lcQuickSync, "mailjd.quick.sync")
15
16 797 QuickCalendarSync::QuickCalendarSync(CalendarSyncCoordinator *calendars,
17 797 ContactSyncCoordinator *contacts, QObject *parent)
18
1/1
✓ Branch 3 → 4 taken 797 times.
797 : QObject(parent), m_calendars(calendars), m_contacts(contacts) {
19
2/2
✓ Branch 5 → 6 taken 794 times.
✓ Branch 5 → 15 taken 3 times.
797 if (m_calendars) {
20
1/1
✓ Branch 7 → 8 taken 794 times.
794 connect(m_calendars->syncStatus(), &CalendarSyncStatus::changed, this, &QuickCalendarSync::changed);
21
1/1
✓ Branch 9 → 10 taken 794 times.
794 connect(m_calendars, &CalendarSyncCoordinator::syncStateChanged, this, &QuickCalendarSync::changed);
22
1/1
✓ Branch 12 → 13 taken 794 times.
794 connect(m_calendars->subscriptions(), &CalendarSubscriptions::changed, this, &QuickCalendarSync::changed);
23 }
24 // "2 min ago" ages while the window is open.
25
1/1
✓ Branch 15 → 16 taken 797 times.
797 m_refresh.setInterval(30000);
26
1/1
✓ Branch 16 → 17 taken 797 times.
797 connect(&m_refresh, &QTimer::timeout, this, &QuickCalendarSync::changed);
27
1/1
✓ Branch 18 → 19 taken 797 times.
797 m_refresh.start();
28 797 }
29
30 48 qint64 QuickCalendarSync::now() const {
31
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 6 taken 44 times.
48 return m_clock ? m_clock() : QDateTime::currentSecsSinceEpoch();
32 }
33
34 4117 bool QuickCalendarSync::running() const {
35
3/4
✓ Branch 2 → 3 taken 4115 times.
✓ Branch 2 → 6 taken 2 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4115 times.
4117 return m_calendars && m_calendars->syncRunning();
36 }
37
38 1748 QString QuickCalendarSync::lastSyncText(qint64 lastSuccess) const {
39
2/2
✓ Branch 2 → 3 taken 1700 times.
✓ Branch 2 → 5 taken 48 times.
1748 if (lastSuccess <= 0)
40
1/1
✓ Branch 3 → 4 taken 1700 times.
1700 return tr("Never synchronized");
41
1/1
✓ Branch 5 → 6 taken 48 times.
48 const qint64 age = qMax<qint64>(0, now() - lastSuccess);
42
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 10 taken 47 times.
48 if (age < 60)
43
1/1
✓ Branch 8 → 9 taken 1 time.
1 return tr("Synchronized just now");
44 // English source texts are final (no English catalog): singular and
45 // plural are separate strings, the German catalog adds its numerus forms.
46
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 15 taken 46 times.
47 if (age < 3600) {
47 1 const int minutes = int(age / 60);
48
2/4
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 1 time.
✗ Branch 12 → 14 not taken.
✓ Branch 13 → 14 taken 1 time.
1 return minutes == 1 ? tr("Synchronized 1 minute ago") : tr("Synchronized %n minutes ago", "", minutes);
49 }
50
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 20 taken 45 times.
46 if (age < 24 * 3600) {
51 1 const int hours = int(age / 3600);
52
2/4
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 1 time.
✗ Branch 17 → 19 not taken.
✓ Branch 18 → 19 taken 1 time.
1 return hours == 1 ? tr("Synchronized 1 hour ago") : tr("Synchronized %n hours ago", "", hours);
53 }
54
1/1
✓ Branch 20 → 21 taken 45 times.
45 const QDateTime when = QDateTime::fromSecsSinceEpoch(lastSuccess);
55
1/1
✓ Branch 21 → 22 taken 45 times.
45 return tr("Last synchronized on %1")
56
3/3
✓ Branch 23 → 24 taken 45 times.
✓ Branch 24 → 25 taken 45 times.
✓ Branch 25 → 26 taken 45 times.
45 .arg(QLocale().toString(when, QLocale::ShortFormat));
57 45 }
58
59 6801 QVariantList QuickCalendarSync::accounts() const {
60 6801 QVariantList result;
61
1/1
✓ Branch 2 → 3 taken 6801 times.
6801 const auto configs = DavAccountSettings::calendarConfigs();
62
5/5
✓ Branch 3 → 4 taken 6801 times.
✓ Branch 4 → 5 taken 6801 times.
✓ Branch 5 → 6 taken 6801 times.
✓ Branch 164 → 7 taken 1743 times.
✓ Branch 164 → 165 taken 6801 times.
8544 for (const auto &account : DavAccountSettings::accounts()) {
63 1743 bool calendarsSelected = false;
64
2/2
✓ Branch 21 → 10 taken 86 times.
✓ Branch 21 → 22 taken 1743 times.
1829 for (const auto &config : configs)
65
3/6
✓ Branch 12 → 13 taken 86 times.
✗ Branch 12 → 16 not taken.
✓ Branch 14 → 15 taken 86 times.
✗ Branch 14 → 16 not taken.
✓ Branch 17 → 18 taken 86 times.
✗ Branch 17 → 19 not taken.
86 if (config.accountId == account.id && !config.selectedCalendars.isEmpty())
66 86 calendarsSelected = true;
67 5223 const CalendarAccountSyncState state = m_calendars ? m_calendars->syncStatus()->state(account.id)
68
5/15
✓ Branch 22 → 23 taken 1740 times.
✓ Branch 22 → 25 taken 3 times.
✓ Branch 24 → 30 taken 1740 times.
✓ Branch 30 → 31 taken 3 times.
✓ Branch 30 → 32 taken 1740 times.
✗ Branch 169 → 170 not taken.
✗ Branch 169 → 172 not taken.
✗ Branch 170 → 171 not taken.
✗ Branch 170 → 172 not taken.
✗ Branch 173 → 174 not taken.
✗ Branch 173 → 175 not taken.
✗ Branch 177 → 178 not taken.
✗ Branch 177 → 180 not taken.
✗ Branch 178 → 179 not taken.
✗ Branch 178 → 180 not taken.
1746 : CalendarAccountSyncState{account.id};
69 1743 QVariantMap entry;
70
1/1
✓ Branch 36 → 37 taken 1743 times.
3486 entry.insert(QStringLiteral("accountId"), account.id);
71
2/2
✓ Branch 40 → 41 taken 1743 times.
✓ Branch 42 → 43 taken 1743 times.
3486 const QString host = QUrl(account.serverUrl).host();
72
3/4
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 1743 times.
✓ Branch 51 → 52 taken 1743 times.
✓ Branch 56 → 57 taken 1743 times.
5229 entry.insert(QStringLiteral("label"), host.isEmpty() ? account.username
73
4/13
✓ Branch 50 → 51 taken 1743 times.
✓ Branch 61 → 62 taken 1743 times.
✗ Branch 61 → 63 not taken.
✓ Branch 63 → 64 taken 1743 times.
✗ Branch 63 → 65 not taken.
✓ Branch 65 → 66 taken 1743 times.
✗ Branch 65 → 67 not taken.
✗ Branch 204 → 205 not taken.
✗ Branch 204 → 206 not taken.
✗ Branch 208 → 209 not taken.
✗ Branch 208 → 210 not taken.
✗ Branch 212 → 213 not taken.
✗ Branch 212 → 214 not taken.
3486 : account.username + QStringLiteral(" @ ") + host);
74
1/1
✓ Branch 71 → 72 taken 1743 times.
3486 entry.insert(QStringLiteral("calendarsSelected"), calendarsSelected);
75
5/7
✓ Branch 75 → 76 taken 86 times.
✓ Branch 75 → 77 taken 1657 times.
✓ Branch 84 → 85 taken 1743 times.
✓ Branch 89 → 90 taken 1657 times.
✓ Branch 89 → 91 taken 86 times.
✗ Branch 237 → 238 not taken.
✗ Branch 237 → 239 not taken.
5143 entry.insert(QStringLiteral("state"), calendarsSelected ? state.state : QStringLiteral("unconfigured"));
76
2/2
✓ Branch 91 → 92 taken 1743 times.
✓ Branch 96 → 97 taken 1743 times.
3486 entry.insert(QStringLiteral("lastSyncText"), lastSyncText(state.lastSuccess));
77
1/1
✓ Branch 105 → 106 taken 1743 times.
3486 entry.insert(QStringLiteral("lastSuccess"), state.lastSuccess);
78
1/1
✓ Branch 113 → 114 taken 1743 times.
3486 entry.insert(QStringLiteral("errorKind"), state.errorKind);
79
1/1
✓ Branch 124 → 125 taken 1743 times.
3486 entry.insert(QStringLiteral("error"), calendarsSelected
80
4/4
✓ Branch 117 → 118 taken 86 times.
✓ Branch 117 → 119 taken 1657 times.
✓ Branch 118 → 120 taken 86 times.
✓ Branch 119 → 120 taken 1657 times.
3486 ? CalendarSyncStatus::messageForKind(state.errorKind)
81 : tr("No calendars are selected for this account. Use Discover to choose them."));
82
1/1
✓ Branch 133 → 134 taken 1743 times.
3486 entry.insert(QStringLiteral("detail"), state.errorDetail);
83
1/2
✓ Branch 142 → 143 taken 1699 times.
✗ Branch 142 → 146 not taken.
3442 const bool needsCredentials = state.errorKind == QLatin1String("password") ||
84
3/4
✓ Branch 139 → 140 taken 1699 times.
✓ Branch 139 → 146 taken 44 times.
✗ Branch 145 → 146 not taken.
✓ Branch 145 → 147 taken 1699 times.
3442 state.errorKind == QLatin1String("authorization") ||
85 1699 state.errorKind == QLatin1String("authentication");
86
1/1
✓ Branch 152 → 153 taken 1743 times.
3486 entry.insert(QStringLiteral("needsCredentials"), needsCredentials);
87
1/1
✓ Branch 157 → 158 taken 1743 times.
1743 result.append(entry);
88 8544 }
89 6801 return result;
90 6801 }
91
92 2159 QString QuickCalendarSync::summaryText() const {
93
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 2159 times.
2159 if (running())
94 ✗ return tr("Synchronizing…");
95
1/1
✓ Branch 6 → 7 taken 2159 times.
2159 const QVariantList list = accounts();
96 2159 qint64 oldest = 0;
97 2159 bool any = false;
98
2/2
✓ Branch 62 → 9 taken 512 times.
✓ Branch 62 → 63 taken 2156 times.
2668 for (const auto &value : list) {
99
1/1
✓ Branch 10 → 11 taken 512 times.
512 const QVariantMap entry = value.toMap();
100
4/4
✓ Branch 15 → 16 taken 512 times.
✓ Branch 16 → 17 taken 512 times.
✓ Branch 21 → 22 taken 509 times.
✓ Branch 21 → 23 taken 3 times.
1024 if (!entry.value(QStringLiteral("calendarsSelected")).toBool())
101 509 continue;
102
3/4
✓ Branch 28 → 29 taken 3 times.
✓ Branch 29 → 30 taken 3 times.
✓ Branch 36 → 37 taken 3 times.
✗ Branch 36 → 39 not taken.
6 if (entry.value(QStringLiteral("state")).toString() == QLatin1String("error"))
103
1/1
✓ Branch 37 → 38 taken 3 times.
3 return tr("Synchronization failed");
104 ✗ const qint64 success = entry.value(QStringLiteral("lastSuccess")).toLongLong();
105 ✗ oldest = any ? qMin(oldest, success) : success;
106 ✗ any = true;
107 512 }
108
1/3
✗ Branch 63 → 64 not taken.
✓ Branch 63 → 65 taken 2156 times.
✗ Branch 64 → 66 not taken.
2156 return any ? lastSyncText(oldest) : QString();
109 2159 }
110
111 1250 QVariantMap QuickCalendarSync::problem() const {
112
5/5
✓ Branch 2 → 3 taken 1250 times.
✓ Branch 3 → 4 taken 1250 times.
✓ Branch 4 → 5 taken 1250 times.
✓ Branch 53 → 6 taken 320 times.
✓ Branch 53 → 54 taken 1247 times.
1567 for (const auto &value : accounts()) {
113
1/3
✓ Branch 7 → 8 taken 320 times.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
320 const QVariantMap entry = value.toMap();
114
10/22
✓ Branch 12 → 13 taken 320 times.
✓ Branch 13 → 14 taken 320 times.
✓ Branch 14 → 15 taken 3 times.
✓ Branch 14 → 25 taken 317 times.
✓ Branch 23 → 24 taken 3 times.
✗ Branch 23 → 25 not taken.
✓ Branch 38 → 39 taken 320 times.
✗ Branch 38 → 40 not taken.
✓ Branch 40 → 41 taken 320 times.
✗ Branch 40 → 42 not taken.
✓ Branch 42 → 43 taken 320 times.
✗ Branch 42 → 44 not taken.
✓ Branch 44 → 45 taken 3 times.
✓ Branch 44 → 46 taken 317 times.
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 85 not taken.
✗ Branch 87 → 88 not taken.
✗ Branch 87 → 89 not taken.
✗ Branch 91 → 92 not taken.
✗ Branch 91 → 93 not taken.
✗ Branch 95 → 96 not taken.
✗ Branch 95 → 97 not taken.
963 if (entry.value(QStringLiteral("calendarsSelected")).toBool() &&
115
13/24
✓ Branch 20 → 21 taken 3 times.
✓ Branch 21 → 22 taken 3 times.
✓ Branch 26 → 27 taken 3 times.
✓ Branch 26 → 28 taken 317 times.
✓ Branch 28 → 29 taken 3 times.
✓ Branch 28 → 30 taken 317 times.
✓ Branch 30 → 31 taken 3 times.
✓ Branch 30 → 32 taken 317 times.
✓ Branch 32 → 33 taken 3 times.
✓ Branch 32 → 34 taken 317 times.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 317 times.
✓ Branch 36 → 37 taken 320 times.
✗ Branch 36 → 38 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 66 → 67 not taken.
✗ Branch 66 → 68 not taken.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
326 entry.value(QStringLiteral("state")).toString() == QLatin1String("error"))
116 3 return entry;
117 1567 }
118 1247 return {};
119 }
120
121 ✗ void QuickCalendarSync::syncNow() {
122 ✗ qCInfo(lcQuickSync) << "Manual synchronization requested";
123 ✗ if (m_calendars) {
124 ✗ m_calendars->retryPendingWrites();
125 ✗ m_calendars->triggerSync();
126 }
127 ✗ if (m_contacts)
128 ✗ m_contacts->triggerSync();
129 ✗ emit changed();
130 ✗ }
131
132 62 QVariantList QuickCalendarSync::subscriptions() const {
133 62 QVariantList list;
134
4/5
✓ Branch 2 → 3 taken 62 times.
✓ Branch 3 → 4 taken 62 times.
✓ Branch 4 → 5 taken 62 times.
✗ Branch 67 → 6 not taken.
✓ Branch 67 → 68 taken 62 times.
62 for (const auto &entry : CalendarSubscriptions::subscriptions()) {
135 // S106-256: "Downloading…" only while this feed's own download runs —
136 // a feed whose downloads all failed showed it whenever another ran.
137 ✗ const bool refreshing = m_calendars && m_calendars->subscriptions()->running(entry.id);
138 ✗ list.append(QVariantMap{{QStringLiteral("accountId"), CalendarSubscriptions::accountIdFor(entry.id)},
139 ✗ {QStringLiteral("name"), entry.name},
140 ✗ {QStringLiteral("url"), entry.url},
141 ✗ {QStringLiteral("intervalMinutes"), entry.intervalMinutes},
142 ✗ {QStringLiteral("stateText"), refreshing && entry.lastRefresh == 0 ? tr("Downloading…")
143 ✗ : lastSyncText(entry.lastRefresh)},
144 ✗ {QStringLiteral("error"), entry.lastError}});
145 62 }
146 62 return list;
147 ✗ }
148
149 1 QVariantMap QuickCalendarSync::subscribe(const QString &url, const QString &name, int intervalHours,
150 const QString &username, const QString &password) {
151
1/7
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 1 time.
✗ Branch 10 → 11 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
1 if (!m_calendars) return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), tr("Calendar storage not available")}};
152 1 QString error;
153
1/1
✓ Branch 22 → 23 taken 1 time.
1 QByteArray secret = password.toUtf8();
154
1/1
✓ Branch 25 → 26 taken 1 time.
1 const QString id = m_calendars->subscriptions()->add(url, name, qMax(1, intervalHours) * 60, &error, username, secret);
155
1/1
✓ Branch 26 → 27 taken 1 time.
1 secret.fill('\0');
156
6/6
✓ Branch 27 → 28 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 32 → 33 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 38 → 29 taken 1 time.
✓ Branch 38 → 39 taken 1 time.
2 qCInfo(lcQuickSync) << "Subscribe:" << !id.isEmpty();
157
2/4
✓ Branch 50 → 51 taken 2 times.
✓ Branch 50 → 52 taken 1 time.
✗ Branch 94 → 95 not taken.
✗ Branch 94 → 96 not taken.
5 return {{QStringLiteral("ok"), !id.isEmpty()}, {QStringLiteral("error"), error}};
158 4 }
159
160 ✗ bool QuickCalendarSync::unsubscribe(const QString &accountId) {
161 ✗ if (!m_calendars || !CalendarSubscriptions::isSubscription(accountId)) return false;
162 ✗ return m_calendars->subscriptions()->remove(accountId.mid(QStringLiteral("subscription:").size()));
163 }
164
165 ✗ bool QuickCalendarSync::updateSubscription(const QString &accountId, const QString &name, int intervalMinutes) {
166 ✗ if (!m_calendars || !CalendarSubscriptions::isSubscription(accountId)) return false;
167 ✗ return m_calendars->subscriptions()->update(accountId.mid(QStringLiteral("subscription:").size()), name,
168 ✗ intervalMinutes);
169 }
170
171 ✗ void QuickCalendarSync::refreshSubscription(const QString &accountId) {
172 ✗ if (m_calendars && CalendarSubscriptions::isSubscription(accountId))
173 ✗ m_calendars->subscriptions()->refresh(accountId.mid(QStringLiteral("subscription:").size()));
174 ✗ }
175