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: 94.4% 458 / 0 / 485
Functions: 100.0% 49 / 0 / 49
Branches: 82.5% 683 / 0 / 828

presentation/QuickDavPush.cpp
Line Branch Exec Source
1 #include "QuickDavPush.h"
2
3 #include "app/core/CalendarSyncCoordinator.h"
4 #include "app/core/CalendarSyncStatus.h"
5 #include "app/core/DavPushStatistics.h"
6 #include "app/core/DavSyncScheduler.h"
7 #include "data/CalendarStore.h"
8 #include "data/DavPushSettings.h"
9 #include "data/DavPushStore.h"
10 #include "service/DavPushServiceTest.h"
11
12 #include <QClipboard>
13 #include <QPointer>
14 #include <QDateTime>
15 #include <QGuiApplication>
16 #include <QLocale>
17 #include <QLoggingCategory>
18 #include <QUrl>
19 #include <algorithm>
20
21
4/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 8 taken 15 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.
17 Q_LOGGING_CATEGORY(lcQuickPush, "mailjd.quick.push")
22
23 namespace {
24 constexpr int kIconPaused = 0xF03E6; // pause-circle-outline
25 constexpr int kIconOffline = 0xF0164; // cloud-off-outline
26 constexpr int kIconSyncAlert = 0xF04E7; // sync-alert
27 constexpr int kIconInterrupted = 0xF0E81; // bell-alert-outline
28 constexpr int kIconSetup = 0xF009C; // bell-outline
29 constexpr int kIconWaiting = 0xF051F; // timer-sand
30 constexpr int kIconConnecting = 0xF031A; // lan-pending
31 constexpr int kIconActive = 0xF140C; // lightning-bolt-outline (F100-05: quiet, "instant")
32 constexpr int kIconNoPush = 0xF0A91; // bell-off-outline
33 constexpr int kIconOff = 0xF13E8; // bell-cancel-outline
34
35 29 bool credentialsKind(const QString &kind) {
36
4/6
✓ Branch 4 → 5 taken 28 times.
✓ Branch 4 → 11 taken 1 time.
✓ Branch 7 → 8 taken 28 times.
✗ Branch 7 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 28 times.
57 return kind == QLatin1String("password") || kind == QLatin1String("authorization") ||
37 57 kind == QLatin1String("authentication");
38 }
39
40 4 QPair<qint64, qint64> medianP95(QList<qint64> values) {
41
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 7 taken 2 times.
4 if (values.isEmpty()) return {-1, -1};
42
3/3
✓ Branch 7 → 8 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
2 std::sort(values.begin(), values.end());
43 4 const auto at = [&](double q) { return values.at(qMin<qsizetype>(values.size() - 1, qsizetype(q * values.size()))); };
44 2 return {at(0.5), at(0.95)};
45 }
46 } // namespace
47
48 // --- model ------------------------------------------------------------------
49
50 397 int QuickDavPushModel::rowCount(const QModelIndex &parent) const {
51
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 397 times.
397 return parent.isValid() ? 0 : int(m_rows.size());
52 }
53
54 218 QVariant QuickDavPushModel::data(const QModelIndex &index, int role) const {
55
3/6
✓ Branch 3 → 4 taken 218 times.
✗ Branch 3 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 218 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 218 times.
218 if (!index.isValid() || index.row() >= m_rows.size()) return {};
56 218 const DavPushRow &row = m_rows.at(index.row());
57
7/13
✓ Branch 13 → 14 taken 48 times.
✗ Branch 13 → 15 not taken.
✗ Branch 13 → 16 not taken.
✓ Branch 13 → 17 taken 48 times.
✓ Branch 13 → 18 taken 24 times.
✓ Branch 13 → 19 taken 24 times.
✓ Branch 13 → 20 taken 1 time.
✓ Branch 13 → 21 taken 48 times.
✗ Branch 13 → 22 not taken.
✗ Branch 13 → 23 not taken.
✗ Branch 13 → 24 not taken.
✓ Branch 13 → 25 taken 25 times.
✗ Branch 13 → 26 not taken.
218 switch (role) {
58 48 case KeyRole: return row.key;
59 ✗ case AccountIdRole: return row.accountId;
60 ✗ case PathRole: return row.path;
61 48 case DisplayNameRole: return row.displayName;
62 24 case AccountLabelRole: return row.accountLabel;
63 24 case AutomaticRole: return row.automatic;
64 1 case StateKeyRole: return row.stateKey;
65 48 case StateTextRole: return row.stateText;
66 ✗ case SeverityRole: return row.severity;
67 ✗ case IconRole: return row.icon;
68 ✗ case DetailTextRole: return row.detailText;
69 25 case KindRole: return row.kind;
70 ✗ default: return {};
71 }
72 }
73
74 8 QHash<int, QByteArray> QuickDavPushModel::roleNames() const {
75 ✗ return {{KeyRole, "calendarKey"}, {AccountIdRole, "accountId"}, {PathRole, "calendarPath"},
76 ✗ {DisplayNameRole, "displayName"}, {AccountLabelRole, "accountLabel"}, {AutomaticRole, "automatic"},
77 ✗ {StateKeyRole, "stateKey"}, {StateTextRole, "stateText"}, {SeverityRole, "severity"},
78
2/4
✓ Branch 16 → 17 taken 96 times.
✓ Branch 16 → 18 taken 8 times.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
104 {IconRole, "icon"}, {DetailTextRole, "detailText"}, {KindRole, "kind"}};
79 8 }
80
81 987 void QuickDavPushModel::setRows(const QList<DavPushRow> &rows) {
82 987 bool sameKeys = rows.size() == m_rows.size();
83
6/6
✓ Branch 9 → 10 taken 985 times.
✓ Branch 9 → 13 taken 76 times.
✓ Branch 11 → 12 taken 74 times.
✓ Branch 11 → 13 taken 911 times.
✓ Branch 14 → 5 taken 74 times.
✓ Branch 14 → 15 taken 987 times.
1061 for (qsizetype i = 0; sameKeys && i < rows.size(); ++i) sameKeys = rows.at(i).key == m_rows.at(i).key;
84
2/2
✓ Branch 15 → 16 taken 76 times.
✓ Branch 15 → 20 taken 911 times.
987 if (!sameKeys) {
85 76 beginResetModel();
86 76 m_rows = rows;
87 76 endResetModel();
88 76 return;
89 }
90
2/2
✓ Branch 39 → 21 taken 74 times.
✓ Branch 39 → 40 taken 911 times.
985 for (qsizetype i = 0; i < rows.size(); ++i) {
91
2/2
✓ Branch 24 → 25 taken 53 times.
✓ Branch 24 → 26 taken 21 times.
74 if (rows.at(i) == m_rows.at(i)) continue;
92 21 m_rows[i] = rows.at(i);
93
3/3
✓ Branch 31 → 32 taken 21 times.
✓ Branch 33 → 34 taken 21 times.
✓ Branch 34 → 35 taken 21 times.
21 emit dataChanged(index(int(i)), index(int(i)));
94 }
95 }
96
97 // --- derivation ---------------------------------------------------------------
98
99 230 DavPushRow QuickDavPush::deriveRow(const DavPushCollectionStatus &status, const DavPushRowContext &context) {
100 230 DavPushRow row;
101
1/1
✓ Branch 2 → 3 taken 230 times.
230 row.key = calendarSelectionKey(status.accountId, status.path);
102
4/4
✓ Branch 6 → 7 taken 90 times.
✓ Branch 6 → 10 taken 140 times.
✓ Branch 13 → 14 taken 90 times.
✓ Branch 13 → 15 taken 140 times.
320 row.kind = status.kind.isEmpty() ? QStringLiteral("calendar") : status.kind;
103 230 const bool book = row.kind == QLatin1String("addressbook");
104 230 row.accountId = status.accountId;
105 230 row.path = status.path;
106 230 row.displayName = status.displayName;
107 230 row.accountLabel = status.accountLabel;
108 230 row.automatic = status.policy == DavPushPolicy::Automatic;
109 241 const auto set = [&row](const char *key, const QString &text, const char *severity, int icon, const QString &detail) {
110
1/1
✓ Branch 3 → 4 taken 241 times.
241 row.stateKey = QLatin1String(key);
111 241 row.stateText = text;
112
1/1
✓ Branch 6 → 7 taken 241 times.
241 row.severity = QLatin1String(severity);
113 241 row.icon = icon;
114 241 row.detailText = detail;
115 241 };
116
1/1
✓ Branch 21 → 22 taken 230 times.
230 const QString interval = tr("MailJD keeps synchronizing every %n minute(s).", "", context.intervalMinutes);
117 // (1) explicit pause / offline
118
2/2
✓ Branch 24 → 25 taken 97 times.
✓ Branch 24 → 31 taken 133 times.
230 if (status.pauseReason == QLatin1String("offline")) {
119
2/2
✓ Branch 26 → 27 taken 97 times.
✓ Branch 27 → 28 taken 97 times.
97 set("offline", tr("Offline · sync paused"), "paused", kIconOffline,
120
1/1
✓ Branch 25 → 26 taken 97 times.
194 tr("MailJD is offline. Changes are synchronized when the connection is back."));
121
6/6
✓ Branch 33 → 34 taken 130 times.
✓ Branch 33 → 37 taken 3 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 36 → 38 taken 128 times.
✓ Branch 39 → 40 taken 5 times.
✓ Branch 39 → 50 taken 128 times.
133 } else if (status.pauseReason == QLatin1String("background-off") || status.pauseReason == QLatin1String("suspended")) {
122
2/2
✓ Branch 45 → 46 taken 5 times.
✓ Branch 46 → 47 taken 5 times.
5 set("paused", tr("Automatic sync paused"), "paused", kIconPaused,
123 5 status.pauseReason == QLatin1String("suspended")
124
4/4
✓ Branch 42 → 43 taken 2 times.
✓ Branch 42 → 44 taken 3 times.
✓ Branch 43 → 45 taken 2 times.
✓ Branch 44 → 45 taken 3 times.
10 ? tr("The computer is going to sleep; synchronization continues after waking up.")
125 : tr("Automatic synchronization is switched off (interval 0). Use Sync now."));
126 // (2) data sync error or local conflict
127
2/2
✓ Branch 51 → 52 taken 4 times.
✓ Branch 51 → 58 taken 124 times.
128 } else if (!context.accountSyncError.isEmpty()) {
128
2/2
✓ Branch 53 → 54 taken 4 times.
✓ Branch 54 → 55 taken 4 times.
4 set("sync-error", tr("Sync failed"), "error", kIconSyncAlert,
129
1/1
✓ Branch 52 → 53 taken 4 times.
8 CalendarSyncStatus::messageForKind(context.accountSyncError));
130
4/4
✓ Branch 58 → 59 taken 120 times.
✓ Branch 58 → 60 taken 4 times.
✓ Branch 59 → 60 taken 2 times.
✓ Branch 59 → 68 taken 118 times.
124 } else if (context.conflicts > 0 || context.pendingWrites > 0) {
131
2/2
✓ Branch 63 → 64 taken 6 times.
✓ Branch 64 → 65 taken 6 times.
6 set("conflict", tr("Local changes waiting"), "warning", kIconSyncAlert,
132
3/3
✓ Branch 60 → 61 taken 4 times.
✓ Branch 60 → 62 taken 2 times.
✓ Branch 61 → 63 taken 4 times.
12 context.conflicts > 0 ? tr("%n change(s) need a decision.", "", context.conflicts)
133
1/1
✓ Branch 62 → 63 taken 2 times.
2 : tr("%n local change(s) are not on the server yet.", "", context.pendingWrites));
134 // push not wanted / not offered (settings and capability, not failures)
135
4/4
✓ Branch 68 → 69 taken 114 times.
✓ Branch 68 → 70 taken 4 times.
✓ Branch 69 → 70 taken 11 times.
✓ Branch 69 → 74 taken 103 times.
118 } else if (!context.pushEnabled || status.policy == DavPushPolicy::IntervalOnly) {
136
2/2
✓ Branch 70 → 71 taken 15 times.
✓ Branch 71 → 72 taken 15 times.
15 set("off", tr("Push off · interval sync"), "neutral", kIconOff, interval);
137
2/2
✓ Branch 74 → 75 taken 7 times.
✓ Branch 74 → 79 taken 96 times.
103 } else if (status.offer.capability == DavPushCapability::Unsupported) {
138
2/2
✓ Branch 75 → 76 taken 7 times.
✓ Branch 76 → 77 taken 7 times.
7 set("unsupported", tr("Server offers no push"), "neutral", kIconNoPush, interval);
139
2/2
✓ Branch 79 → 80 taken 2 times.
✓ Branch 79 → 88 taken 94 times.
96 } else if (status.offer.capability == DavPushCapability::Incompatible) {
140
2/2
✓ Branch 82 → 83 taken 2 times.
✓ Branch 83 → 84 taken 2 times.
2 set("unsupported", tr("Server offers no usable push"), "neutral", kIconNoPush,
141
2/2
✓ Branch 80 → 81 taken 2 times.
✓ Branch 81 → 82 taken 2 times.
4 tr("The server offers push in a form MailJD cannot use (%1). %2").arg(status.offer.reason, interval));
142
2/2
✓ Branch 88 → 89 taken 27 times.
✓ Branch 88 → 97 taken 67 times.
94 } else if (status.offer.capability != DavPushCapability::Supported) {
143
2/2
✓ Branch 92 → 93 taken 27 times.
✓ Branch 93 → 94 taken 27 times.
27 set("checking", tr("Checking push…"), "info", kIconWaiting,
144
4/4
✓ Branch 89 → 90 taken 1 time.
✓ Branch 89 → 91 taken 26 times.
✓ Branch 90 → 92 taken 1 time.
✓ Branch 91 → 92 taken 26 times.
54 book ? tr("MailJD checks whether the server offers push for this address book.")
145 : tr("MailJD checks whether the server offers push for this calendar."));
146 // (3) push failure with fallback
147
2/2
✓ Branch 97 → 98 taken 65 times.
✓ Branch 97 → 119 taken 2 times.
67 } else if (context.receiverConfigured &&
148
4/4
✓ Branch 98 → 99 taken 53 times.
✓ Branch 98 → 101 taken 12 times.
✓ Branch 99 → 100 taken 50 times.
✓ Branch 99 → 101 taken 3 times.
65 (status.receiver == DavPushReceiverState::Backoff || status.receiver == DavPushReceiverState::Failed ||
149
2/2
✓ Branch 100 → 101 taken 2 times.
✓ Branch 100 → 119 taken 48 times.
50 status.registration == DavPushRegistrationState::Failed)) {
150
2/2
✓ Branch 102 → 103 taken 3 times.
✓ Branch 102 → 105 taken 2 times.
5 const QString cause = status.receiver == DavPushReceiverState::Backoff || status.receiver == DavPushReceiverState::Failed
151
2/2
✓ Branch 101 → 102 taken 5 times.
✓ Branch 101 → 103 taken 12 times.
17 ? tr("The push service does not respond.")
152
2/2
✓ Branch 103 → 104 taken 15 times.
✓ Branch 105 → 106 taken 2 times.
17 : tr("The server did not accept the push subscription.");
153
2/2
✓ Branch 111 → 112 taken 17 times.
✓ Branch 112 → 113 taken 17 times.
17 set("interrupted", tr("Push interrupted · interval sync active"), "warning", kIconInterrupted,
154
3/3
✓ Branch 106 → 107 taken 17 times.
✓ Branch 109 → 110 taken 17 times.
✓ Branch 110 → 111 taken 17 times.
34 cause + QLatin1Char(' ') + tr("MailJD keeps syncing at the interval."));
155 // (4) setup / connection needed
156
2/2
✓ Branch 119 → 120 taken 2 times.
✓ Branch 119 → 128 taken 48 times.
67 } else if (!context.receiverConfigured) {
157
2/2
✓ Branch 123 → 124 taken 2 times.
✓ Branch 124 → 125 taken 2 times.
2 set("setup", tr("Set up a push service"), "info", kIconSetup,
158
4/4
✓ Branch 120 → 121 taken 1 time.
✓ Branch 120 → 122 taken 1 time.
✓ Branch 121 → 123 taken 1 time.
✓ Branch 122 → 123 taken 1 time.
4 book ? tr("This address book supports push. Choose a push service in the DAV settings.")
159 : tr("This calendar supports push. Choose a push service in the DAV settings."));
160
2/2
✓ Branch 128 → 129 taken 37 times.
✓ Branch 128 → 145 taken 11 times.
48 } else if (status.registration == DavPushRegistrationState::Active &&
161
1/2
✓ Branch 129 → 130 taken 37 times.
✗ Branch 129 → 145 not taken.
37 status.receiver == DavPushReceiverState::Connected) {
162 // (5) normal operation
163
3/4
✓ Branch 130 → 131 taken 12 times.
✓ Branch 130 → 133 taken 25 times.
✓ Branch 131 → 132 taken 12 times.
✗ Branch 131 → 133 not taken.
37 const bool received = status.lastPushAt > 0 && status.lastPushAt * 1000 >= status.receiverConnectedSince;
164
3/3
✓ Branch 138 → 139 taken 12 times.
✓ Branch 138 → 140 taken 25 times.
✓ Branch 141 → 142 taken 37 times.
37 set(received ? "active-received" : "active",
165
4/4
✓ Branch 135 → 136 taken 12 times.
✓ Branch 135 → 137 taken 25 times.
✓ Branch 136 → 138 taken 12 times.
✓ Branch 137 → 138 taken 25 times.
74 received ? tr("Push active · change received") : tr("Push active"), "ok", kIconActive,
166
1/1
✓ Branch 134 → 135 taken 37 times.
74 tr("Changes arrive within seconds while MailJD is running."));
167 37 } else {
168
2/2
✓ Branch 148 → 149 taken 11 times.
✓ Branch 149 → 150 taken 11 times.
11 set("connecting", tr("Connecting push…"), "info", kIconConnecting,
169
4/4
✓ Branch 145 → 146 taken 1 time.
✓ Branch 145 → 147 taken 10 times.
✓ Branch 146 → 148 taken 1 time.
✓ Branch 147 → 148 taken 10 times.
22 book ? tr("MailJD subscribes this address book and connects to the push service.")
170 : tr("MailJD subscribes this calendar and connects to the push service."));
171 }
172
8/8
✓ Branch 153 → 154 taken 134 times.
✓ Branch 153 → 161 taken 96 times.
✓ Branch 156 → 157 taken 129 times.
✓ Branch 156 → 160 taken 5 times.
✓ Branch 159 → 160 taken 6 times.
✓ Branch 159 → 161 taken 123 times.
✓ Branch 162 → 163 taken 11 times.
✓ Branch 162 → 167 taken 219 times.
230 if (status.lastSyncAt == 0 && (row.severity == QLatin1String("ok") || row.severity == QLatin1String("neutral")))
173
2/2
✓ Branch 163 → 164 taken 11 times.
✓ Branch 164 → 165 taken 11 times.
11 set("never", tr("Not synchronized yet"), "info", kIconWaiting, row.detailText);
174 230 return row;
175 230 }
176
177 // --- QuickDavPush ---------------------------------------------------------
178
179 806 QuickDavPush::QuickDavPush(DavPushCoordinator *coordinator, CalendarSyncCoordinator *calendar, QObject *parent)
180
5/5
✓ Branch 3 → 4 taken 806 times.
✓ Branch 4 → 5 taken 806 times.
✓ Branch 5 → 6 taken 806 times.
✓ Branch 7 → 8 taken 806 times.
✓ Branch 8 → 9 taken 806 times.
1612 : QObject(parent), m_coordinator(coordinator), m_calendar(calendar) {
181
1/1
✓ Branch 16 → 17 taken 806 times.
806 m_coalesce.setSingleShot(true);
182
1/1
✓ Branch 17 → 18 taken 806 times.
806 m_coalesce.setInterval(150);
183
1/1
✓ Branch 18 → 19 taken 806 times.
806 connect(&m_coalesce, &QTimer::timeout, this, &QuickDavPush::refresh);
184
1/1
✓ Branch 20 → 21 taken 806 times.
806 m_age.setInterval(30000); // relative texts age
185
1/1
✓ Branch 21 → 22 taken 806 times.
806 connect(&m_age, &QTimer::timeout, this, &QuickDavPush::refresh);
186
1/1
✓ Branch 23 → 24 taken 806 times.
806 m_age.start();
187
3/3
✓ Branch 25 → 26 taken 796 times.
✓ Branch 25 → 31 taken 10 times.
✓ Branch 28 → 29 taken 796 times.
806 if (m_coordinator) connect(m_coordinator, &DavPushCoordinator::changed, &m_coalesce, qOverload<>(&QTimer::start));
188
2/2
✓ Branch 32 → 33 taken 795 times.
✓ Branch 32 → 47 taken 11 times.
806 if (m_calendar) {
189
1/1
✓ Branch 35 → 36 taken 795 times.
795 connect(m_calendar, &CalendarSyncCoordinator::syncStateChanged, &m_coalesce, qOverload<>(&QTimer::start));
190
1/1
✓ Branch 40 → 41 taken 795 times.
795 connect(m_calendar->syncStatus(), &CalendarSyncStatus::changed, &m_coalesce, qOverload<>(&QTimer::start));
191
1/1
✓ Branch 44 → 45 taken 795 times.
795 connect(m_calendar, &CalendarSyncCoordinator::storeChanged, &m_coalesce, qOverload<>(&QTimer::start));
192 }
193
1/1
✓ Branch 47 → 48 taken 806 times.
806 refresh();
194 806 }
195
196 1601 QuickDavPush::~QuickDavPush() = default;
197
198 69 qint64 QuickDavPush::now() const {
199
2/2
✓ Branch 3 → 4 taken 61 times.
✓ Branch 3 → 6 taken 8 times.
69 return m_clock ? m_clock() : QDateTime::currentSecsSinceEpoch();
200 }
201
202 200 DavPushRowContext QuickDavPush::contextFor(const DavPushCollectionStatus &status) const {
203 200 DavPushRowContext context;
204
3/5
✓ Branch 3 → 4 taken 118 times.
✓ Branch 3 → 8 taken 82 times.
✓ Branch 5 → 6 taken 118 times.
✗ Branch 68 → 69 not taken.
✗ Branch 68 → 70 not taken.
318 context.receiverConfigured = m_coordinator ? m_coordinator->effectiveReceiver().isConfigured()
205
5/7
✓ Branch 8 → 9 taken 82 times.
✓ Branch 11 → 12 taken 82 times.
✓ Branch 11 → 13 taken 118 times.
✓ Branch 13 → 14 taken 118 times.
✓ Branch 13 → 15 taken 82 times.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
200 : DavPushSettings::receiver().isConfigured();
206
1/1
✓ Branch 15 → 16 taken 200 times.
200 context.pushEnabled = DavPushSettings::enabled();
207 200 context.intervalMinutes =
208
3/3
✓ Branch 16 → 17 taken 125 times.
✓ Branch 16 → 18 taken 75 times.
✓ Branch 18 → 19 taken 75 times.
200 qMax(1, status.intervalMinutes > 0 ? status.intervalMinutes : CalendarSyncCoordinator::configuredIntervalMinutes());
209 // Data sync errors and the write outbox are the calendar's; address
210 // books report push state only (their sync status lives in contacts).
211
6/6
✓ Branch 22 → 23 taken 104 times.
✓ Branch 22 → 27 taken 96 times.
✓ Branch 25 → 26 taken 7 times.
✓ Branch 25 → 27 taken 97 times.
✓ Branch 28 → 29 taken 7 times.
✓ Branch 28 → 62 taken 193 times.
200 if (m_calendar && status.kind != QLatin1String("addressbook")) {
212
1/1
✓ Branch 31 → 32 taken 7 times.
7 const auto state = m_calendar->syncStatus()->state(status.accountId);
213
2/2
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 4 times.
7 if (state.state == QLatin1String("error")) context.accountSyncError = state.errorKind;
214
5/5
✓ Branch 38 → 39 taken 7 times.
✓ Branch 39 → 40 taken 7 times.
✓ Branch 40 → 41 taken 7 times.
✓ Branch 58 → 42 taken 4 times.
✓ Branch 58 → 59 taken 7 times.
11 for (const auto &entry : m_calendar->store()->outboxEntries()) {
215
5/6
✓ Branch 44 → 45 taken 3 times.
✓ Branch 44 → 47 taken 1 time.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 3 times.
✓ Branch 49 → 50 taken 1 time.
✓ Branch 49 → 51 taken 3 times.
4 if (entry.accountId != status.accountId || entry.calendarPath != status.path) continue;
216
2/2
✓ Branch 53 → 54 taken 2 times.
✓ Branch 53 → 55 taken 1 time.
3 if (entry.state == QLatin1String("conflict")) ++context.conflicts;
217 1 else ++context.pendingWrites;
218 7 }
219 7 }
220 200 return context;
221 ✗ }
222
223 984 void QuickDavPush::refresh() {
224
4/4
✓ Branch 3 → 4 taken 27 times.
✓ Branch 3 → 5 taken 957 times.
✓ Branch 4 → 10 taken 27 times.
✓ Branch 8 → 10 taken 939 times.
2925 m_statuses = m_statusProvider ? m_statusProvider()
225
2/2
✓ Branch 6 → 7 taken 939 times.
✓ Branch 6 → 9 taken 18 times.
957 : m_coordinator ? m_coordinator->statuses()
226 984 : QList<DavPushCollectionStatus>();
227 984 QList<DavPushRow> rows;
228
5/5
✓ Branch 16 → 17 taken 171 times.
✓ Branch 17 → 18 taken 171 times.
✓ Branch 18 → 19 taken 171 times.
✓ Branch 23 → 15 taken 171 times.
✓ Branch 23 → 24 taken 984 times.
1155 for (const auto &status : std::as_const(m_statuses)) rows.append(deriveRow(status, contextFor(status)));
229
1/1
✓ Branch 24 → 25 taken 984 times.
984 m_model.setRows(rows);
230 984 ++m_revision;
231
1/1
✓ Branch 25 → 26 taken 984 times.
984 emit changed();
232 984 }
233
234 997 bool QuickDavPush::enabled() const { return DavPushSettings::enabled(); }
235
236 7 void QuickDavPush::setEnabled(bool on) {
237
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 6 times.
7 if (on == enabled()) return;
238 6 DavPushSettings::setEnabled(on);
239
8/8
✓ Branch 6 → 7 taken 6 times.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 11 → 12 taken 6 times.
✓ Branch 12 → 13 taken 3 times.
✓ Branch 12 → 14 taken 3 times.
✓ Branch 15 → 16 taken 6 times.
✓ Branch 19 → 8 taken 6 times.
✓ Branch 19 → 20 taken 6 times.
12 qCInfo(lcQuickPush) << "Push" << (on ? "enabled" : "disabled");
240
2/2
✓ Branch 21 → 22 taken 4 times.
✓ Branch 21 → 24 taken 2 times.
6 if (m_coordinator) m_coordinator->reconfigure();
241 6 refresh();
242 }
243
244
1/1
✓ Branch 2 → 3 taken 89 times.
89 bool QuickDavPush::serviceConfigured() const { return DavPushSettings::receiver().isConfigured(); }
245
1/1
✓ Branch 2 → 3 taken 18 times.
18 QString QuickDavPush::serviceServer() const { return DavPushSettings::receiver().server; }
246
1/1
✓ Branch 2 → 3 taken 19 times.
19 bool QuickDavPush::serviceHasToken() const { return DavPushSettings::receiver().hasToken; }
247 2 QString QuickDavPush::publicServer() const { return DavPushSettings::kPublicNtfyServer; }
248
249 35 bool QuickDavPush::systemServiceDetected() const {
250
6/6
✓ Branch 3 → 4 taken 34 times.
✓ Branch 3 → 9 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 33 times.
✓ Branch 11 → 12 taken 34 times.
✓ Branch 11 → 13 taken 1 time.
35 return m_coordinator && !m_coordinator->systemDistributor().isEmpty();
251 }
252
253 36 QString QuickDavPush::systemServiceName() const {
254
2/2
✓ Branch 3 → 4 taken 35 times.
✓ Branch 3 → 6 taken 1 time.
36 const QString name = m_coordinator ? m_coordinator->systemDistributor() : QString();
255
2/2
✓ Branch 8 → 9 taken 34 times.
✓ Branch 8 → 10 taken 2 times.
36 if (name.isEmpty()) return {};
256
3/3
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 15 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
2 if (name == QLatin1String("org.unifiedpush.Distributor.kde")) return tr("KDE (KUnifiedPush)");
257
1/1
✓ Branch 18 → 19 taken 1 time.
1 return name.mid(name.lastIndexOf(QLatin1Char('.')) + 1);
258 36 }
259
260 39 bool QuickDavPush::useSystemService() const { return DavPushSettings::useSystemService(); }
261
262 5 void QuickDavPush::setUseSystemService(bool use) {
263
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
5 if (use == useSystemService()) return;
264 5 DavPushSettings::setUseSystemService(use);
265
8/8
✓ Branch 6 → 7 taken 5 times.
✓ Branch 10 → 11 taken 5 times.
✓ Branch 11 → 12 taken 5 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 3 times.
✓ Branch 15 → 16 taken 5 times.
✓ Branch 19 → 8 taken 5 times.
✓ Branch 19 → 20 taken 5 times.
10 qCInfo(lcQuickPush) << "System push service" << (use ? "allowed" : "not used");
266
2/2
✓ Branch 21 → 22 taken 3 times.
✓ Branch 21 → 24 taken 2 times.
5 if (m_coordinator) m_coordinator->reconfigure();
267 5 refresh();
268 }
269
270 39 bool QuickDavPush::systemServiceActive() const {
271
7/9
✓ Branch 3 → 4 taken 35 times.
✓ Branch 3 → 9 taken 4 times.
✓ Branch 5 → 6 taken 35 times.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 34 times.
✓ Branch 11 → 12 taken 35 times.
✓ Branch 11 → 13 taken 4 times.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
39 return m_coordinator && m_coordinator->effectiveReceiver().provider == DavPushSettings::kSystemProvider;
272 }
273
274 38 QString QuickDavPush::serviceKind() const {
275
1/1
✓ Branch 2 → 3 taken 38 times.
38 const auto receiver = DavPushSettings::receiver();
276
2/2
✓ Branch 4 → 5 taken 36 times.
✓ Branch 4 → 6 taken 2 times.
38 if (!receiver.isConfigured()) return {};
277
6/6
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 10 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 1 time.
6 return receiver.publicService ? QStringLiteral("public") : QStringLiteral("own");
278 38 }
279
280 3 void QuickDavPush::testService(const QString &kind, const QString &server, const QString &token) {
281
5/6
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 2 times.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 6 not taken.
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 2 times.
4 if (m_test && m_test->running()) return; // a repeated click joins the running test
282
2/3
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
2 const QString origin = DavPushSettings::normaliseServer(kind == QLatin1String("public") ? publicServer() : server);
283
2/2
✓ Branch 17 → 18 taken 1 time.
✓ Branch 17 → 29 taken 1 time.
2 if (origin.isEmpty()) {
284 1 m_testState = QStringLiteral("failed");
285
1/1
✓ Branch 24 → 25 taken 1 time.
1 m_testMessage = tr("Enter the address of an HTTPS push service, e.g. https://ntfy.example.org.");
286
1/1
✓ Branch 27 → 28 taken 1 time.
1 emit testChanged();
287 1 return;
288 }
289
1/2
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 37 not taken.
1 if (!m_test) {
290
3/6
✓ Branch 30 → 31 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 1 time.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
1 m_test = new DavPushServiceTest(this);
291
1/1
✓ Branch 34 → 35 taken 1 time.
1 connect(m_test, &DavPushServiceTest::finished, this, [this](DavPushServiceTest::Result result, const QString &) {
292
3/6
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 13 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1 time.
2 m_testState = result == DavPushServiceTest::Result::Ok ? QStringLiteral("ok") : QStringLiteral("failed");
293
1/6
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 20 not taken.
✗ Branch 15 → 24 not taken.
✗ Branch 15 → 28 not taken.
✗ Branch 15 → 32 not taken.
✓ Branch 15 → 36 taken 1 time.
1 switch (result) {
294 ✗ case DavPushServiceTest::Result::Ok:
295 ✗ m_testMessage = tr("The push service works: a test message went through it and came back.");
296 ✗ break;
297 ✗ case DavPushServiceTest::Result::Tls:
298 ✗ m_testMessage = tr("The push service's certificate is not trusted.");
299 ✗ break;
300 ✗ case DavPushServiceTest::Result::Authentication:
301 ✗ m_testMessage = tr("The push service rejected the sign-in. Check the token.");
302 ✗ break;
303 ✗ case DavPushServiceTest::Result::NoDelivery:
304 ✗ m_testMessage = tr("The push service is reachable but did not deliver the test message.");
305 ✗ break;
306 ✗ case DavPushServiceTest::Result::InvalidUrl:
307 ✗ m_testMessage = tr("Enter the address of an HTTPS push service, e.g. https://ntfy.example.org.");
308 ✗ break;
309 1 default:
310
1/1
✓ Branch 36 → 37 taken 1 time.
1 m_testMessage = tr("The push service cannot be reached.");
311 }
312 1 emit testChanged();
313 1 });
314 }
315 1 m_testState = QStringLiteral("testing");
316
1/1
✓ Branch 43 → 44 taken 1 time.
1 m_testMessage = tr("Testing the push service…");
317
1/1
✓ Branch 46 → 47 taken 1 time.
1 emit testChanged();
318
2/2
✓ Branch 47 → 48 taken 1 time.
✓ Branch 48 → 49 taken 1 time.
1 m_test->start(origin, token.toUtf8());
319 2 }
320
321 12 void QuickDavPush::cancelTest() {
322
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 12 times.
12 if (m_test) m_test->cancel();
323 12 m_testState = QStringLiteral("idle");
324 12 m_testMessage.clear();
325 12 emit testChanged();
326 12 }
327
328 7 QVariantMap QuickDavPush::saveService(const QString &kind, const QString &server, const QString &token) {
329 7 DavPushSettings::Receiver receiver;
330 7 receiver.provider = QStringLiteral("ntfy");
331 7 receiver.publicService = kind == QLatin1String("public");
332
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 6 times.
7 receiver.server = receiver.publicService ? publicServer() : server;
333
6/17
✓ Branch 16 → 17 taken 3 times.
✓ Branch 16 → 23 taken 4 times.
✓ Branch 17 → 18 taken 3 times.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 24 taken 3 times.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 7 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 7 times.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 129 not taken.
✗ Branch 131 → 132 not taken.
✗ Branch 131 → 133 not taken.
7 receiver.hasToken = !token.isEmpty() || (serviceHasToken() && DavPushSettings::normaliseServer(receiver.server) == serviceServer());
334 // S106-336: a token is saved only where it can be kept; the settings
335 // never claim a stored token whose write was skipped or failed.
336
8/9
✓ Branch 30 → 31 taken 4 times.
✓ Branch 30 → 37 taken 3 times.
✓ Branch 32 → 33 taken 3 times.
✓ Branch 32 → 36 taken 1 time.
✓ Branch 34 → 35 taken 3 times.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 3 times.
✓ Branch 38 → 39 taken 1 time.
✓ Branch 38 → 58 taken 6 times.
7 if (!token.isEmpty() && !(m_coordinator && m_coordinator->isOpen()))
337 2 return {{QStringLiteral("ok"), false},
338
3/5
✓ Branch 46 → 47 taken 1 time.
✓ Branch 50 → 51 taken 2 times.
✓ Branch 50 → 52 taken 1 time.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
5 {QStringLiteral("error"), tr("The token cannot be stored right now: the push storage is not available.")}};
339 6 QString error;
340
3/3
✓ Branch 58 → 59 taken 6 times.
✓ Branch 59 → 60 taken 1 time.
✓ Branch 59 → 79 taken 5 times.
6 if (!DavPushSettings::setReceiver(receiver, &error))
341
3/5
✓ Branch 67 → 68 taken 1 time.
✓ Branch 71 → 72 taken 2 times.
✓ Branch 71 → 73 taken 1 time.
✗ Branch 162 → 163 not taken.
✗ Branch 162 → 164 not taken.
6 return {{QStringLiteral("ok"), false}, {QStringLiteral("error"), tr("Enter the address of an HTTPS push service, e.g. https://ntfy.example.org.")}};
342
2/2
✓ Branch 80 → 81 taken 3 times.
✓ Branch 80 → 93 taken 2 times.
5 if (!token.isEmpty()) {
343
1/1
✓ Branch 81 → 82 taken 3 times.
3 QPointer<QuickDavPush> self(this);
344
3/5
✓ Branch 85 → 86 taken 3 times.
✓ Branch 86 → 87 taken 3 times.
✓ Branch 87 → 88 taken 3 times.
✗ Branch 194 → 195 not taken.
✗ Branch 194 → 196 not taken.
3 m_coordinator->storeProviderToken(token.toUtf8(), [self, receiver](bool ok) mutable {
345
3/6
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (ok || !self) return;
346 1 receiver.hasToken = false;
347 1 QString ignored;
348
1/1
✓ Branch 9 → 10 taken 1 time.
1 DavPushSettings::setReceiver(receiver, &ignored);
349
5/5
✓ Branch 10 → 11 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 19 → 12 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
2 qCWarning(lcQuickPush) << "Push service token not stored; the service is kept without a token";
350
1/1
✓ Branch 21 → 22 taken 1 time.
1 self->refresh();
351 1 });
352 3 }
353
8/8
✓ Branch 93 → 94 taken 5 times.
✓ Branch 97 → 98 taken 5 times.
✓ Branch 98 → 99 taken 5 times.
✓ Branch 99 → 100 taken 1 time.
✓ Branch 99 → 101 taken 4 times.
✓ Branch 102 → 103 taken 5 times.
✓ Branch 106 → 95 taken 5 times.
✓ Branch 106 → 107 taken 5 times.
10 qCInfo(lcQuickPush) << "Push service saved:" << (receiver.publicService ? "public" : "own");
354
3/3
✓ Branch 108 → 109 taken 3 times.
✓ Branch 108 → 111 taken 2 times.
✓ Branch 110 → 111 taken 3 times.
5 if (m_coordinator) m_coordinator->reconfigure();
355
1/1
✓ Branch 111 → 112 taken 5 times.
5 refresh();
356
2/4
✓ Branch 118 → 119 taken 5 times.
✓ Branch 118 → 120 taken 5 times.
✗ Branch 208 → 209 not taken.
✗ Branch 208 → 210 not taken.
20 return {{QStringLiteral("ok"), true}};
357 30 }
358
359 2 void QuickDavPush::removeService() {
360 2 DavPushSettings::clearReceiver();
361
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 7 taken 1 time.
2 if (m_coordinator) m_coordinator->removeProviderToken(); // S106-337
362
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 1 time.
2 if (m_coordinator) m_coordinator->reconfigure();
363 2 refresh();
364 2 }
365
366 3 bool QuickDavPush::setAutomatic(const QString &key, bool automatic) {
367
1/1
✓ Branch 2 → 3 taken 3 times.
3 const auto status = statusOf(key);
368
5/6
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 1 time.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 2 times.
3 if (!status || !m_coordinator) return false;
369
3/3
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 1 time.
✓ Branch 17 → 18 taken 2 times.
2 m_coordinator->setPolicy(status->accountId, status->path,
370 automatic ? DavPushPolicy::Automatic : DavPushPolicy::IntervalOnly);
371
1/1
✓ Branch 18 → 19 taken 2 times.
2 refresh();
372 2 return true;
373 3 }
374
375 2 void QuickDavPush::syncNow(const QString &key) {
376
6/7
✓ Branch 2 → 3 taken 2 times.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 8 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 14 taken 1 time.
2 if (const auto status = statusOf(key); status && m_coordinator)
377
1/1
✓ Branch 13 → 14 taken 1 time.
2 m_coordinator->syncCollectionNow(status->accountId, status->path);
378 2 }
379
380 2 void QuickDavPush::detectAgain(const QString &key) {
381
7/8
✓ Branch 2 → 3 taken 2 times.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 8 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 13 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
2 if (const auto status = statusOf(key); status && m_coordinator) m_coordinator->detectAgain(status->accountId);
382 2 }
383
384 2 void QuickDavPush::reconnect(const QString &accountId) {
385
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 6 taken 1 time.
2 if (m_coordinator) m_coordinator->reconnect(accountId);
386 2 }
387
388 2 bool QuickDavPush::resetStatistics(const QString &key) {
389
1/1
✓ Branch 2 → 3 taken 2 times.
2 const auto status = statusOf(key);
390
5/7
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 13 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 13 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 13 not taken.
2 const bool ok = status && m_coordinator && m_coordinator->resetStatistics(status->accountId, status->path);
391
1/1
✓ Branch 14 → 15 taken 2 times.
2 refresh();
392 2 return ok;
393 2 }
394
395 2 bool QuickDavPush::copyDiagnostics() {
396
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 1 time.
2 if (!m_coordinator) return false;
397
1/1
✓ Branch 6 → 7 taken 1 time.
1 const QString report = m_coordinator->diagnosticsReport();
398
4/5
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
1 if (auto *app = qobject_cast<QGuiApplication *>(QCoreApplication::instance())) app->clipboard()->setText(report);
399
1/1
✓ Branch 12 → 13 taken 1 time.
1 emit diagnosticsReady(report);
400 1 return true;
401 1 }
402
403 39 std::optional<DavPushCollectionStatus> QuickDavPush::statusOf(const QString &key) const {
404
2/2
✓ Branch 12 → 4 taken 44 times.
✓ Branch 12 → 13 taken 5 times.
49 for (const auto &status : m_statuses)
405
3/3
✓ Branch 5 → 6 taken 44 times.
✓ Branch 8 → 9 taken 34 times.
✓ Branch 8 → 10 taken 10 times.
44 if (calendarSelectionKey(status.accountId, status.path) == key) return status;
406 5 return std::nullopt;
407 }
408
409 2064 QVariantMap QuickDavPush::statusFor(const QString &key, int) const {
410
2/2
✓ Branch 50 → 5 taken 459 times.
✓ Branch 50 → 51 taken 2012 times.
2471 for (const auto &row : m_model.rows()) {
411
2/2
✓ Branch 7 → 8 taken 407 times.
✓ Branch 7 → 9 taken 52 times.
459 if (row.key != key) continue;
412 104 return {{QStringLiteral("stateKey"), row.stateKey},
413 104 {QStringLiteral("stateText"), row.stateText},
414 104 {QStringLiteral("severity"), row.severity},
415 104 {QStringLiteral("icon"), row.icon},
416
4/6
✓ Branch 28 → 29 taken 52 times.
✓ Branch 29 → 30 taken 52 times.
✓ Branch 33 → 34 taken 260 times.
✓ Branch 33 → 35 taken 52 times.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
416 {QStringLiteral("accessibleName"), tr("%1: %2").arg(row.displayName, row.stateText)}};
417 }
418 2012 return {};
419 312 }
420
421 1034 QString QuickDavPush::summaryFor(const QVariantList &keys, int) const {
422 1034 int total = 0;
423 1034 int active = 0;
424 1034 int problems = 0;
425 1034 bool books = false;
426
2/2
✓ Branch 31 → 5 taken 201 times.
✓ Branch 31 → 32 taken 1034 times.
1235 for (const auto &row : m_model.rows()) {
427
2/2
✓ Branch 7 → 8 taken 147 times.
✓ Branch 7 → 9 taken 54 times.
201 if (!keys.contains(row.key)) continue;
428 54 ++total;
429
3/4
✓ Branch 9 → 10 taken 54 times.
✗ Branch 9 → 13 not taken.
✓ Branch 12 → 13 taken 7 times.
✓ Branch 12 → 14 taken 47 times.
54 books = books || row.kind == QLatin1String("addressbook");
430
3/3
✓ Branch 16 → 17 taken 54 times.
✓ Branch 17 → 18 taken 23 times.
✓ Branch 17 → 19 taken 31 times.
54 if (row.stateKey.startsWith(QLatin1String("active"))) ++active;
431
5/6
✓ Branch 21 → 22 taken 47 times.
✓ Branch 21 → 25 taken 7 times.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 47 times.
✓ Branch 27 → 28 taken 7 times.
✓ Branch 27 → 29 taken 47 times.
54 if (row.severity == QLatin1String("warning") || row.severity == QLatin1String("error")) ++problems;
432 }
433
2/2
✓ Branch 32 → 33 taken 1024 times.
✓ Branch 32 → 34 taken 10 times.
1034 if (total == 0) return {};
434
1/3
✓ Branch 35 → 36 taken 7 times.
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 71 not taken.
14 QString text = books ? tr("Push active for %1 of %n calendar(s) and address book(s)", "", total).arg(active)
435
9/11
✓ Branch 34 → 35 taken 7 times.
✓ Branch 34 → 38 taken 3 times.
✓ Branch 37 → 41 taken 7 times.
✓ Branch 38 → 39 taken 3 times.
✓ Branch 40 → 41 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
✓ Branch 41 → 43 taken 7 times.
✓ Branch 43 → 44 taken 7 times.
✓ Branch 43 → 45 taken 3 times.
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 66 not taken.
17 : tr("Push active for %1 of %n calendar(s)", "", total).arg(active);
436
5/5
✓ Branch 45 → 46 taken 7 times.
✓ Branch 45 → 57 taken 3 times.
✓ Branch 46 → 47 taken 7 times.
✓ Branch 50 → 51 taken 7 times.
✓ Branch 51 → 52 taken 7 times.
17 if (problems > 0) text += QStringLiteral(" · ") + tr("%n with problems", "", problems);
437 10 return text;
438 10 }
439
440 1010 QString QuickDavPush::summaryText() const {
441 1010 int active = 0;
442 1010 bool interrupted = false;
443 // The calendar/task sync line: calendars only.
444
2/2
✓ Branch 20 → 5 taken 134 times.
✓ Branch 20 → 21 taken 1010 times.
1144 for (const auto &row : m_model.rows()) {
445
2/2
✓ Branch 8 → 9 taken 108 times.
✓ Branch 8 → 10 taken 26 times.
134 if (row.kind != QLatin1String("calendar")) continue;
446
3/3
✓ Branch 11 → 12 taken 26 times.
✓ Branch 12 → 13 taken 9 times.
✓ Branch 12 → 14 taken 17 times.
26 if (row.stateKey.startsWith(QLatin1String("active"))) ++active;
447
2/2
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 18 taken 22 times.
26 if (row.stateKey == QLatin1String("interrupted")) interrupted = true;
448 }
449
2/2
✓ Branch 21 → 22 taken 4 times.
✓ Branch 21 → 23 taken 1006 times.
1010 if (interrupted) return tr("Push interrupted · interval sync active");
450
2/2
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 25 taken 1005 times.
1006 if (active > 0) return tr("Push connected for %n calendar(s)", "", active);
451 1005 return {};
452 }
453
454 73 QVariantList QuickDavPush::accounts() const {
455 73 QVariantList result;
456 73 QStringList seen;
457
2/2
✓ Branch 75 → 5 taken 51 times.
✓ Branch 75 → 76 taken 73 times.
124 for (const auto &row : m_model.rows()) {
458
2/2
✓ Branch 7 → 8 taken 43 times.
✓ Branch 7 → 9 taken 8 times.
51 if (seen.contains(row.accountId)) continue;
459
1/1
✓ Branch 9 → 10 taken 8 times.
8 seen << row.accountId;
460 8 QVariantList keys;
461 8 QString problem;
462
2/2
✓ Branch 34 → 13 taken 54 times.
✓ Branch 34 → 35 taken 8 times.
62 for (const auto &other : m_model.rows()) {
463
2/2
✓ Branch 15 → 16 taken 3 times.
✓ Branch 15 → 17 taken 51 times.
54 if (other.accountId != row.accountId) continue;
464
1/1
✓ Branch 18 → 19 taken 51 times.
51 keys << other.key;
465
7/8
✓ Branch 21 → 22 taken 21 times.
✓ Branch 21 → 29 taken 30 times.
✓ Branch 24 → 25 taken 14 times.
✓ Branch 24 → 28 taken 7 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 14 times.
✓ Branch 30 → 31 taken 7 times.
✓ Branch 30 → 32 taken 44 times.
51 if (problem.isEmpty() && (other.severity == QLatin1String("warning") || other.severity == QLatin1String("error")))
466 7 problem = other.detailText;
467 }
468
1/1
✓ Branch 54 → 55 taken 8 times.
24 result.append(QVariantMap{{QStringLiteral("accountId"), row.accountId},
469 16 {QStringLiteral("label"), row.accountLabel},
470
1/1
✓ Branch 46 → 47 taken 8 times.
16 {QStringLiteral("summary"), summaryFor(keys)},
471
2/4
✓ Branch 58 → 59 taken 32 times.
✓ Branch 58 → 60 taken 8 times.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 90 not taken.
48 {QStringLiteral("problem"), problem}});
472 8 }
473 73 return result;
474 113 }
475
476 64 QString QuickDavPush::timeText(qint64 epochSeconds) const {
477
3/3
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 5 taken 60 times.
✓ Branch 3 → 4 taken 4 times.
64 if (epochSeconds <= 0) return tr("Never");
478
1/1
✓ Branch 5 → 6 taken 60 times.
60 const QDateTime when = QDateTime::fromSecsSinceEpoch(epochSeconds);
479
3/3
✓ Branch 6 → 7 taken 60 times.
✓ Branch 7 → 8 taken 60 times.
✓ Branch 8 → 9 taken 60 times.
60 const QDate today = QDateTime::fromSecsSinceEpoch(now()).date();
480
8/8
✓ Branch 10 → 11 taken 60 times.
✓ Branch 12 → 13 taken 32 times.
✓ Branch 12 → 24 taken 28 times.
✓ Branch 13 → 14 taken 32 times.
✓ Branch 15 → 16 taken 32 times.
✓ Branch 16 → 17 taken 32 times.
✓ Branch 17 → 18 taken 32 times.
✓ Branch 18 → 19 taken 32 times.
92 if (when.date() == today) return tr("Today, %1").arg(QLocale().toString(when.time(), QLocale::ShortFormat));
481
2/2
✓ Branch 24 → 25 taken 28 times.
✓ Branch 25 → 26 taken 28 times.
28 return QLocale().toString(when, QLocale::ShortFormat);
482 60 }
483
484 7 QString QuickDavPush::relativeText(qint64 epochSeconds) const {
485 7 const qint64 delta = epochSeconds - now();
486
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 5 taken 3 times.
7 if (delta <= 60) return tr("in less than a minute");
487
2/2
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 2 times.
3 if (delta < 3600) return tr("in %n minute(s)", "", int(delta / 60));
488
3/3
✓ Branch 7 → 8 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 10 → 11 taken 2 times.
4 return tr("at %1").arg(timeText(epochSeconds));
489 }
490
491 29 QVariantMap QuickDavPush::statisticsFor(const DavPushCollectionStatus &status) const {
492 29 QVariantMap stats;
493
5/6
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 7 taken 27 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 27 times.
✓ Branch 9 → 11 taken 2 times.
29 if (!m_coordinator || !m_coordinator->statistics()) return stats;
494 2 const auto *s = m_coordinator->statistics();
495
1/1
✓ Branch 13 → 14 taken 2 times.
2 const QString scope = DavPushStatistics::collectionScope(status.accountId, status.path);
496
1/1
✓ Branch 14 → 15 taken 2 times.
2 const QString account = DavPushStatistics::accountScope(status.accountId);
497
1/1
✓ Branch 15 → 16 taken 2 times.
2 const qint64 dayAgo = now() - 24 * 3600;
498 2 QVariantList counters;
499 32 const auto add = [&](const QString &label, const QString &scopeName, const char *name) {
500
1/1
✓ Branch 22 → 23 taken 32 times.
96 counters.append(QVariantMap{{QStringLiteral("label"), label},
501
2/2
✓ Branch 10 → 11 taken 32 times.
✓ Branch 11 → 12 taken 32 times.
64 {QStringLiteral("total"), s->counter(scopeName, QLatin1String(name))},
502
4/6
✓ Branch 17 → 18 taken 32 times.
✓ Branch 18 → 19 taken 32 times.
✓ Branch 26 → 27 taken 96 times.
✓ Branch 26 → 28 taken 32 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
160 {QStringLiteral("day"), s->periodSum(scopeName, QLatin1String(name), dayAgo)}});
503
1/5
✓ Branch 20 → 21 taken 32 times.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 77 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 75 → 77 not taken.
160 };
504
3/5
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 2 times.
✗ Branch 19 → 21 not taken.
✓ Branch 20 → 21 taken 2 times.
✓ Branch 21 → 22 taken 2 times.
2 add(status.kind == QLatin1String("addressbook") ? tr("Changes received for this address book")
505 : tr("Changes received for this calendar"),
506 scope, "messages.mapped");
507
2/2
✓ Branch 23 → 24 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
2 add(tr("Notices already applied (same sync token)"), scope, "messages.token-noop");
508
2/2
✓ Branch 26 → 27 taken 2 times.
✓ Branch 27 → 28 taken 2 times.
2 add(tr("Notices combined into one sync"), scope, "messages.coalesced");
509
2/2
✓ Branch 29 → 30 taken 2 times.
✓ Branch 30 → 31 taken 2 times.
2 add(tr("Syncs after a push notice"), scope, "syncs.push");
510
2/2
✓ Branch 32 → 33 taken 2 times.
✓ Branch 33 → 34 taken 2 times.
2 add(tr("Safety syncs (interval)"), scope, "syncs.poll");
511
2/2
✓ Branch 35 → 36 taken 2 times.
✓ Branch 36 → 37 taken 2 times.
2 add(tr("Catch-up syncs (recovery)"), scope, "syncs.recovery");
512
2/2
✓ Branch 38 → 39 taken 2 times.
✓ Branch 39 → 40 taken 2 times.
2 add(tr("Manual syncs"), scope, "syncs.manual");
513
2/2
✓ Branch 41 → 42 taken 2 times.
✓ Branch 42 → 43 taken 2 times.
2 add(tr("Failed syncs"), scope, "syncs.failed");
514
2/2
✓ Branch 44 → 45 taken 2 times.
✓ Branch 45 → 46 taken 2 times.
2 add(tr("Subscriptions registered"), scope, "subscription.registered");
515
2/2
✓ Branch 47 → 48 taken 2 times.
✓ Branch 48 → 49 taken 2 times.
2 add(tr("Subscriptions renewed"), scope, "subscription.renewed");
516
2/2
✓ Branch 50 → 51 taken 2 times.
✓ Branch 51 → 52 taken 2 times.
2 add(tr("Subscription errors"), scope, "subscription.failed");
517
2/2
✓ Branch 53 → 54 taken 2 times.
✓ Branch 54 → 55 taken 2 times.
2 add(tr("Messages received by the account (all calendars and address books)"), account, "messages.received");
518
2/2
✓ Branch 56 → 57 taken 2 times.
✓ Branch 57 → 58 taken 2 times.
2 add(tr("Messages discarded: unknown topic"), account, "messages.discarded.unknown-topic");
519
2/2
✓ Branch 59 → 60 taken 2 times.
✓ Branch 60 → 61 taken 2 times.
2 add(tr("Messages discarded: not decryptable"), account, "messages.discarded.decrypt");
520
2/2
✓ Branch 62 → 63 taken 2 times.
✓ Branch 63 → 64 taken 2 times.
2 add(tr("Push service reconnects"), account, "receiver.reconnects");
521
2/2
✓ Branch 65 → 66 taken 2 times.
✓ Branch 66 → 67 taken 2 times.
2 add(tr("Fallback periods"), account, "fallback.entered");
522
1/1
✓ Branch 72 → 73 taken 2 times.
4 stats.insert(QStringLiteral("counters"), counters);
523 2 QVariantList latencies;
524
1/1
✓ Branch 79 → 80 taken 2 times.
14 for (const auto &[kind, label] : {std::pair{QStringLiteral("push-start"), tr("Push → sync start")},
525
3/7
✓ Branch 84 → 85 taken 2 times.
✓ Branch 143 → 95 taken 4 times.
✓ Branch 143 → 144 taken 2 times.
✗ Branch 310 → 311 not taken.
✗ Branch 310 → 314 not taken.
✗ Branch 312 → 313 not taken.
✗ Branch 312 → 314 not taken.
18 std::pair{QStringLiteral("push-commit"), tr("Push → data stored")}}) {
526
1/1
✓ Branch 97 → 98 taken 4 times.
4 const auto samples = s->latencies(scope, kind);
527
1/1
✓ Branch 99 → 100 taken 4 times.
4 const auto [median, p95] = medianP95(samples);
528
1/1
✓ Branch 124 → 125 taken 4 times.
12 latencies.append(QVariantMap{{QStringLiteral("label"), label},
529 8 {QStringLiteral("text"), samples.isEmpty()
530
4/4
✓ Branch 111 → 112 taken 2 times.
✓ Branch 111 → 113 taken 2 times.
✓ Branch 112 → 121 taken 2 times.
✓ Branch 120 → 121 taken 2 times.
10 ? tr("not measured yet")
531
11/19
✓ Branch 113 → 114 taken 2 times.
✓ Branch 115 → 116 taken 2 times.
✓ Branch 117 → 118 taken 2 times.
✓ Branch 128 → 129 taken 8 times.
✓ Branch 128 → 130 taken 4 times.
✓ Branch 131 → 132 taken 2 times.
✓ Branch 131 → 133 taken 2 times.
✓ Branch 133 → 134 taken 2 times.
✓ Branch 133 → 135 taken 2 times.
✓ Branch 135 → 136 taken 2 times.
✓ Branch 135 → 137 taken 2 times.
✗ Branch 323 → 324 not taken.
✗ Branch 323 → 325 not taken.
✗ Branch 329 → 330 not taken.
✗ Branch 329 → 331 not taken.
✗ Branch 334 → 335 not taken.
✗ Branch 334 → 336 not taken.
✗ Branch 339 → 340 not taken.
✗ Branch 339 → 341 not taken.
18 : tr("median %1 ms · p95 %2 ms · n = %3").arg(median).arg(p95).arg(samples.size())}});
532 10 }
533
1/1
✓ Branch 154 → 155 taken 2 times.
4 stats.insert(QStringLiteral("latencies"), latencies);
534
1/1
✓ Branch 160 → 161 taken 2 times.
2 const qint64 since = m_coordinator->store()->statisticsSince(scope);
535
5/15
✗ Branch 161 → 162 not taken.
✓ Branch 161 → 166 taken 2 times.
✗ Branch 162 → 163 not taken.
✗ Branch 164 → 165 not taken.
✗ Branch 165 → 167 not taken.
✓ Branch 166 → 167 taken 2 times.
✓ Branch 171 → 172 taken 2 times.
✗ Branch 176 → 177 not taken.
✓ Branch 176 → 178 taken 2 times.
✗ Branch 178 → 179 not taken.
✓ Branch 178 → 180 taken 2 times.
✗ Branch 394 → 395 not taken.
✗ Branch 394 → 396 not taken.
✗ Branch 399 → 400 not taken.
✗ Branch 399 → 401 not taken.
4 stats.insert(QStringLiteral("sinceText"), since > 0 ? tr("Counting since %1").arg(timeText(since)) : tr("Nothing counted yet"));
536 2 QVariantList events;
537
5/6
✓ Branch 182 → 183 taken 2 times.
✓ Branch 183 → 184 taken 2 times.
✓ Branch 185 → 186 taken 2 times.
✓ Branch 186 → 187 taken 2 times.
✗ Branch 219 → 188 not taken.
✓ Branch 219 → 220 taken 2 times.
2 for (const auto &event : m_coordinator->store()->events(scope).mid(0, 10))
538 ✗ events.append(QVariantMap{{QStringLiteral("time"), timeText(event.at)}, {QStringLiteral("kind"), event.kind},
539
0/4
✗ Branch 208 → 209 not taken.
✗ Branch 208 → 210 not taken.
✗ Branch 413 → 414 not taken.
✗ Branch 413 → 415 not taken.
2 {QStringLiteral("detail"), event.detail}});
540
1/1
✓ Branch 225 → 226 taken 2 times.
4 stats.insert(QStringLiteral("events"), events);
541 2 return stats;
542 14 }
543
544 30 QVariantMap QuickDavPush::details(const QString &key, int) const {
545
1/1
✓ Branch 2 → 3 taken 30 times.
30 const auto status = statusOf(key);
546
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 29 times.
30 if (!status) return {};
547
1/1
✓ Branch 7 → 8 taken 29 times.
29 const DavPushRowContext context = contextFor(*status);
548
1/1
✓ Branch 9 → 10 taken 29 times.
29 const DavPushRow row = deriveRow(*status, context);
549 29 QString offer;
550
5/5
✓ Branch 11 → 12 taken 18 times.
✓ Branch 11 → 16 taken 1 time.
✓ Branch 11 → 20 taken 1 time.
✓ Branch 11 → 24 taken 1 time.
✓ Branch 11 → 28 taken 8 times.
29 switch (status->offer.capability) {
551
1/1
✓ Branch 12 → 13 taken 18 times.
18 case DavPushCapability::Supported: offer = tr("Push supported"); break;
552
1/1
✓ Branch 16 → 17 taken 1 time.
1 case DavPushCapability::Unsupported: offer = tr("No push offered"); break;
553
1/1
✓ Branch 20 → 21 taken 1 time.
1 case DavPushCapability::Incompatible: offer = tr("Push offered, not usable"); break;
554
1/1
✓ Branch 24 → 25 taken 1 time.
1 case DavPushCapability::ProbeError: offer = tr("Check failed; tried again later"); break;
555
1/1
✓ Branch 28 → 29 taken 8 times.
8 default: offer = tr("Not checked yet"); break;
556 }
557 29 QString receiver;
558
6/6
✓ Branch 33 → 34 taken 9 times.
✓ Branch 33 → 44 taken 1 time.
✓ Branch 33 → 48 taken 5 times.
✓ Branch 33 → 58 taken 1 time.
✓ Branch 33 → 62 taken 10 times.
✓ Branch 33 → 66 taken 3 times.
29 switch (status->receiver) {
559 9 case DavPushReceiverState::Connected:
560
3/3
✓ Branch 34 → 35 taken 9 times.
✓ Branch 37 → 38 taken 9 times.
✓ Branch 38 → 39 taken 9 times.
18 receiver = tr("Connected (since %1)").arg(timeText(status->receiverConnectedSince / 1000));
561 9 break;
562
1/1
✓ Branch 44 → 45 taken 1 time.
1 case DavPushReceiverState::Connecting: receiver = tr("Connecting…"); break;
563 5 case DavPushReceiverState::Backoff:
564
3/3
✓ Branch 48 → 49 taken 5 times.
✓ Branch 51 → 52 taken 5 times.
✓ Branch 52 → 53 taken 5 times.
10 receiver = tr("Interrupted; next attempt %1").arg(relativeText(status->receiverNextAttempt / 1000));
565 5 break;
566
1/1
✓ Branch 58 → 59 taken 1 time.
1 case DavPushReceiverState::Failed: receiver = tr("Needs attention"); break;
567
1/1
✓ Branch 62 → 63 taken 10 times.
10 case DavPushReceiverState::NotConfigured: receiver = tr("No push service chosen"); break;
568
1/1
✓ Branch 66 → 67 taken 3 times.
3 default: receiver = tr("Not running"); break;
569 }
570 ✗ QString registration;
571
5/5
✓ Branch 71 → 72 taken 12 times.
✓ Branch 71 → 82 taken 1 time.
✓ Branch 71 → 86 taken 1 time.
✓ Branch 71 → 90 taken 2 times.
✓ Branch 71 → 100 taken 13 times.
29 switch (status->registration) {
572
3/3
✓ Branch 72 → 73 taken 12 times.
✓ Branch 75 → 76 taken 12 times.
✓ Branch 76 → 77 taken 12 times.
24 case DavPushRegistrationState::Active: registration = tr("Subscribed until %1").arg(timeText(status->expiresAt)); break;
573
1/1
✓ Branch 82 → 83 taken 1 time.
1 case DavPushRegistrationState::Registering: registration = tr("Subscribing…"); break;
574
1/1
✓ Branch 86 → 87 taken 1 time.
1 case DavPushRegistrationState::Removing: registration = tr("Removing the subscription…"); break;
575 2 case DavPushRegistrationState::Failed:
576
3/3
✓ Branch 90 → 91 taken 2 times.
✓ Branch 93 → 94 taken 2 times.
✓ Branch 94 → 95 taken 2 times.
4 registration = tr("Failed; next attempt %1").arg(relativeText(status->nextRetryAt));
577 2 break;
578
1/1
✓ Branch 100 → 101 taken 13 times.
13 default: registration = tr("Not subscribed"); break;
579 }
580 29 qint64 nextPollMs = -1;
581
7/8
✓ Branch 107 → 108 taken 27 times.
✓ Branch 107 → 114 taken 2 times.
✓ Branch 109 → 110 taken 2 times.
✓ Branch 109 → 114 taken 25 times.
✓ Branch 112 → 113 taken 2 times.
✗ Branch 112 → 114 not taken.
✓ Branch 115 → 116 taken 2 times.
✓ Branch 115 → 120 taken 27 times.
29 if (status->kind != QLatin1String("addressbook") && m_calendar && m_calendar->scheduler())
582
1/1
✓ Branch 118 → 119 taken 2 times.
2 nextPollMs = m_calendar->scheduler()->msUntilNextPoll();
583 58 return {{QStringLiteral("key"), key},
584 58 {QStringLiteral("kind"), row.kind},
585 58 {QStringLiteral("accountId"), status->accountId},
586 58 {QStringLiteral("title"), status->displayName},
587 58 {QStringLiteral("accountLabel"), status->accountLabel},
588 58 {QStringLiteral("automatic"), status->policy == DavPushPolicy::Automatic},
589
4/4
✓ Branch 152 → 153 taken 23 times.
✓ Branch 152 → 154 taken 6 times.
✓ Branch 153 → 155 taken 23 times.
✓ Branch 154 → 155 taken 6 times.
58 {QStringLiteral("modeText"), status->policy == DavPushPolicy::Automatic ? tr("Automatic") : tr("Interval only")},
590 58 {QStringLiteral("offerText"), offer},
591 58 {QStringLiteral("receiverText"), receiver},
592 58 {QStringLiteral("registrationText"), registration},
593
4/4
✓ Branch 172 → 173 taken 20 times.
✓ Branch 172 → 175 taken 9 times.
✓ Branch 174 → 176 taken 20 times.
✓ Branch 175 → 176 taken 9 times.
58 {QStringLiteral("lastMessageText"), status->lastPushAt > 0 ? timeText(status->lastPushAt) : tr("No change received yet")},
594
10/14
✓ Branch 181 → 182 taken 21 times.
✓ Branch 181 → 187 taken 8 times.
✓ Branch 182 → 183 taken 21 times.
✓ Branch 185 → 186 taken 21 times.
✓ Branch 186 → 188 taken 21 times.
✓ Branch 187 → 188 taken 8 times.
✓ Branch 297 → 298 taken 21 times.
✓ Branch 297 → 299 taken 8 times.
✓ Branch 299 → 300 taken 21 times.
✓ Branch 299 → 301 taken 8 times.
✗ Branch 470 → 471 not taken.
✗ Branch 470 → 472 not taken.
✗ Branch 475 → 476 not taken.
✗ Branch 475 → 477 not taken.
58 {QStringLiteral("dataText"), status->lastSyncAt > 0 ? tr("Updated successfully at %1").arg(timeText(status->lastSyncAt))
595 : tr("Not synchronized yet")},
596
1/3
✓ Branch 192 → 193 taken 29 times.
✗ Branch 192 → 194 not taken.
✗ Branch 198 → 199 not taken.
58 {QStringLiteral("nextText"), nextPollMs < 0 ? QString()
597
2/11
✗ Branch 194 → 195 not taken.
✗ Branch 196 → 197 not taken.
✗ Branch 197 → 198 not taken.
✗ Branch 290 → 291 not taken.
✓ Branch 290 → 292 taken 29 times.
✗ Branch 292 → 293 not taken.
✓ Branch 292 → 294 taken 29 times.
✗ Branch 452 → 453 not taken.
✗ Branch 452 → 454 not taken.
✗ Branch 457 → 458 not taken.
✗ Branch 457 → 459 not taken.
29 : tr("Safety sync %1").arg(relativeText(now() + nextPollMs / 1000))},
598 58 {QStringLiteral("stateKey"), row.stateKey},
599 58 {QStringLiteral("stateText"), row.stateText},
600 58 {QStringLiteral("detailText"), row.detailText},
601 58 {QStringLiteral("severity"), row.severity},
602 87 {QStringLiteral("errorText"), status->registrationError.isEmpty() ? status->receiverDetail : status->registrationError},
603 58 {QStringLiteral("needsCredentials"), credentialsKind(context.accountSyncError)},
604
2/2
✓ Branch 237 → 238 taken 23 times.
✓ Branch 237 → 244 taken 6 times.
87 {QStringLiteral("canReconnect"), context.receiverConfigured && status->policy == DavPushPolicy::Automatic &&
605
4/6
✓ Branch 235 → 236 taken 29 times.
✗ Branch 235 → 244 not taken.
✓ Branch 239 → 240 taken 17 times.
✓ Branch 239 → 244 taken 6 times.
✓ Branch 242 → 243 taken 17 times.
✗ Branch 242 → 244 not taken.
75 status->offer.capability == DavPushCapability::Supported &&
606 17 status->pauseReason.isEmpty()},
607 58 {QStringLiteral("canSync"), status->pauseReason != QLatin1String("offline")},
608 58 {QStringLiteral("syncing"), status->syncRunning},
609
3/5
✓ Branch 262 → 263 taken 29 times.
✓ Branch 266 → 267 taken 667 times.
✓ Branch 266 → 268 taken 29 times.
✗ Branch 380 → 381 not taken.
✗ Branch 380 → 382 not taken.
754 {QStringLiteral("statistics"), statisticsFor(*status)}};
610 755 }
611