MailJDTourDownloadDocsQualityTestsCoverage

Which source lines and branches the tests execute. Lines 91.5 %, branches 75.0 % (minimum 85 % and 72 %). main @ 399ffe3d · 21 October 2026 · pipeline 7238.

GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 4 / 0 / 4
Functions: 100.0% 4 / 0 / 4
Branches: -% 0 / 0 / 0

app/core/DavPushCoordinator.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QHash>
4 #include <QObject>
5 #include <QPointer>
6 #include <QSet>
7 #include <QTimer>
8 #include <functional>
9 #include <memory>
10 #include <vector>
11
12 #include "CalendarAccountSync.h"
13 #include "DavPushAccount.h"
14 #include "DavPushSource.h"
15 #include "data/DavPushModels.h"
16
17 class DavPushStatistics;
18 class DavPushStore;
19 class DavPushSecretStore;
20 class NetworkLifecycleCoordinator;
21
22 // One calendar row of the push status model (presentation input). Only the
23 // separate state dimensions; texts and priority are derived by the
24 // presentation (SPRINT-96.md §5.2).
25 struct DavPushCollectionStatus {
26 QString accountId;
27 QString path;
28 QString kind; // DavPushSource::kind(): "calendar" or "addressbook"
29 int intervalMinutes = 0; // the source's background interval
30 QString displayName;
31 QString accountLabel;
32 bool readOnly = false;
33 DavPushPolicy policy = DavPushPolicy::Automatic;
34 DavPushOffer offer;
35 qint64 checkedAt = 0;
36 DavPushRegistrationState registration = DavPushRegistrationState::None;
37 qint64 expiresAt = 0;
38 QString registrationError;
39 qint64 nextRetryAt = 0;
40 DavPushReceiverState receiver = DavPushReceiverState::NotConfigured;
41 QString receiverDetail;
42 qint64 receiverConnectedSince = 0; // epoch ms
43 qint64 receiverLastActivity = 0; // epoch ms
44 qint64 receiverNextAttempt = 0; // epoch ms
45 QString pauseReason; // "", "offline", "suspended", "background-off", "disabled"
46 qint64 lastPushAt = 0;
47 qint64 lastSyncAt = 0;
48 bool inFallback = false;
49 bool syncRunning = false;
50 };
51
52 // Sprint 96 S96.2.4–S96.2.6: session-owned DAV-Push automation. Created by
53 // MailSessionCore only in the process that holds the profile lease, so a
54 // second start (which forwards to the owner) registers nothing; opening or
55 // closing windows does not touch subscriptions.
56 //
57 // It computes, per account, the desired push state from the settings
58 // (receiver, policies, application switch, background interval), the
59 // discovered offers and the lifecycle (network gate, suspend) and lets
60 // DavPushAccount converge. Collections come from DavPushSource adapters
61 // (calendars, address books); push notices become targeted read requests
62 // at the source that owns the collection, and push failures switch the
63 // account's sources to their fallback polling.
64 class DavPushCoordinator final : public QObject {
65 Q_OBJECT
66 public:
67 struct Deps {
68 // Owned; at least one. More can follow with addSource() before start().
69 std::vector<std::unique_ptr<DavPushSource>> sources;
70 std::unique_ptr<DavPushSecretStore> secrets;
71 std::function<bool()> networkAllowed;
72 std::function<PushReceiver *(const QString &provider, QObject *parent)> makeReceiver;
73 // The UnifiedPush distributor on the session bus ("" = none). Default:
74 // UnifiedPushReceiver::findDistributor() plus a bus watcher.
75 std::function<QString()> findSystemDistributor;
76 std::function<void(const QString &provider, const QString &server, const QString &channel)> releaseReceiver;
77 std::function<DavPushClient *(const QString &, const QString &, const QString &)> makeClient;
78 CalendarAccountSync::CredentialResolver resolveCredentials;
79 std::function<qint64()> now; // epoch seconds
80 NetworkLifecycleCoordinator *lifecycle = nullptr;
81 };
82
83 DavPushCoordinator(const QString &storePath, Deps deps, QObject *parent = nullptr);
84 ~DavPushCoordinator() override;
85
86 bool isOpen() const;
87 // A further collection kind (e.g. address books, whose sync is created
88 // after the calendar's). Only before start().
89 void addSource(std::unique_ptr<DavPushSource> source);
90 void start();
91 // Regular quit: close the streams, keep valid registrations for the next start.
92 void shutdown();
93 // Settings, accounts, selections or the interval changed.
94 void reconfigure();
95
96 void setPolicy(const QString &accountId, const QString &path, DavPushPolicy policy);
97 void reconnect(const QString &accountId);
98 void detectAgain(const QString &accountId);
99 void syncCollectionNow(const QString &accountId, const QString &path);
100 bool resetStatistics(const QString &accountId, const QString &path);
101 // Optional subscriber token of the push service: keyring only; running
102 // receivers reconnect with it.
103 // `done(ok)` reports whether the token is in the keyring (S106-336); a
104 // coordinator without its store reports false at once.
105 void storeProviderToken(const QByteArray &token, std::function<void(bool)> done = {});
106 // S106-337 (INV-09): removing the push service deletes its token too.
107 void removeProviderToken();
108
109 QList<DavPushCollectionStatus> statuses() const;
110 // The push service in use: the system distributor (KUnifiedPush) when one
111 // runs and the user allows it, else the configured ntfy service.
112 DavPushSettings::Receiver effectiveReceiver() const;
113 // The detected system distributor's bus name ("" = none).
114 69 QString systemDistributor() const { return m_systemDistributor; }
115 128 DavPushStatistics *statistics() const { return m_stats.get(); }
116 16 DavPushStore *store() const { return m_store.get(); }
117 // "", "disabled", "background-off", "offline" or "suspended".
118 QString pauseReason() const;
119 4 qint64 now() const { return m_deps.now(); }
120 // Redacted diagnostics: no channel/endpoint/registration URLs, no keys,
121 // no tokens, no calendar contents.
122 QString diagnosticsReport() const;
123
124 signals:
125 void changed();
126
127 private:
128 void connectSource(DavPushSource *source);
129 void onDiscovered(DavPushSource *source, const QString &accountId, const QList<DavPushDiscovery> &found);
130 void onSyncFinished(const DavPushSyncReport &report);
131 void onBatchStarted(const QString &accountId, const QStringList &forcedPaths);
132 void onPushHealth(const QString &accountId, bool failing);
133 DavPushAccount *accountFor(const QString &accountId);
134 // The source whose selected collections contain the path (nullptr: none).
135 DavPushSource *sourceFor(const QString &accountId, const QString &path) const;
136 // Selected collection paths of every source for the account.
137 QSet<QString> selectedPaths(const QString &accountId) const;
138 void requestSync(const QString &accountId, const QStringList &paths, unsigned reasons);
139
140 std::unique_ptr<DavPushStore> m_store;
141 std::unique_ptr<DavPushStatistics> m_stats;
142 Deps m_deps;
143 QString m_installationId;
144 QHash<QString, QPointer<DavPushAccount>> m_accounts;
145 QTimer m_reconcileTimer;
146 QString m_systemDistributor;
147 bool m_started = false;
148 bool m_suspended = false;
149 };
150