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% 23 / 0 / 23
Functions: 100.0% 19 / 0 / 19
Branches: 75.0% 3 / 0 / 4

service/ImapPushSession.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QAbstractSocket>
4 #include <QElapsedTimer>
5 #include <QString>
6 #include <QStringList>
7
8 #include <functional>
9 #include <optional>
10
11 #include "service/ImapResponseParser.h"
12 #include "service/ImapService.h"
13
14 class ImapCommandTable;
15 class QObject;
16 class QSslSocket;
17 class QTimer;
18
19 // Push session and liveness of one IMAP connection (Sprint 92 S92.1.4,
20 // extracted from ImapService): IDLE (enter, 25-minute renew, DONE and its
21 // round-trip watchdog), NOTIFY SET/NONE, the push restart after commands
22 // (restartPush drains queued work first), keep-alive NOOP and the
23 // liveness probe of ConnectionHealthMonitor (NOOP with a watchdog, or an
24 // IDLE DONE/OK round trip). Part E's detection rules live here.
25 //
26 // Owner: ImapService (std::unique_ptr, at the former position of the IDLE
27 // renew timer and both watchdogs, which it creates in the same order),
28 // connection thread. Plain class; the service constructor connects its
29 // timers to it at the former positions. State changes, connection failure,
30 // the capability checks and E2E phases go through Deps; commands use the
31 // command table; the session state is read by reference.
32 class ImapPushSession {
33 public:
34 using State = ImapService::State;
35 using DisconnectReason = ImapService::DisconnectReason;
36
37 struct Deps {
38 std::function<void(State state)> setState;
39 std::function<void(const QString &error, DisconnectReason reason)> failConnection;
40 std::function<bool()> hasIdleCapability;
41 std::function<bool()> hasNotifyCapability;
42 // S92.5.2: milliseconds since the connection last received data.
43 std::function<qint64()> quietMs;
44 // MAILJD_E2E_TESTING only (present in every build for a stable layout).
45 std::function<void(const QString &phase, quint64 requestToken)> reportE2eTransportPhase;
46 };
47 ImapPushSession(QObject *owner, QSslSocket *socket, ImapCommandTable &commands,
48 const State &state, Deps deps);
49
50 void startIdle();
51 void stopIdle();
52 void onIdleRenew();
53 void restartPush(); // T-320: restart NOTIFY or IDLE based on capabilities
54 void startNotify(const QStringList &subscribedFolders);
55 void stopNotify();
56 void executeAfterNotify(std::function<void()> command);
57 void sendNoop();
58 void requestLivenessProbe(const QString &reason);
59 void onLivenessProbeTimeout();
60 void onIdleRenewWatchdogTimeout();
61
3/4
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 6 taken 125 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 6 not taken.
127 bool usesNotifyPush() const { return m_allowNotifyPush && hasNotifyCapability(); }
62 // S92.5.3: RTT-aware liveness budget. Samples come from the probe NOOP and
63 // the IDLE DONE/OK round trip; a new session starts without samples.
64 void noteRoundTrip(qint64 elapsedMs);
65 int probeWatchdogMs() const;
66 std::optional<double> smoothedRttMs() const { return m_smoothedRttMs; }
67 void resetRoundTrip();
68 // S92.5.2: probe first when a user-visible command meets a quiet connection.
69 bool probeBeforeUse(const char *command);
70 // Quiet time that makes a user-visible command probe first. Below the
71 // secondary monitors' 120 s probe interval, so an idle body/search
72 // connection is checked when it is used.
73 static constexpr qint64 PROBE_BEFORE_USE_QUIET_MS = 20000;
74 // S92.5.6: another connection of the account found the network path dead.
75 // Keep this one if it received data within PATH_ALIVE_MS; fail it when a
76 // command is waiting in the silent path; probe it otherwise.
77 void verifyAfterPathFailure(const QString &reason);
78 static constexpr qint64 PATH_ALIVE_MS = 3000;
79 // Tagged completions of IDLE, NOTIFY SET and NOTIFY NONE.
80 void handleTaggedIdle(const TaggedResponse &response);
81 void handleTaggedNotify(const TaggedResponse &response);
82 void handleTaggedNotifyNone(const TaggedResponse &response);
83
84 // Push/probe state shared with the service (single owner: this unit).
85 3945 bool &isIdling() { return m_isIdling; }
86 2968 bool &idleStartPending() { return m_idleStartPending; }
87 157 QString &idleTag() { return m_idleTag; }
88 1514 bool &isNotifying() { return m_isNotifying; }
89 142 QString &notifyTag() { return m_notifyTag; }
90 2 QStringList &notifyFolders() { return m_notifyFolders; }
91 97 bool &autoIdle() { return m_autoIdle; }
92 3 bool &allowNotifyPush() { return m_allowNotifyPush; }
93 2928 QString &probeTag() { return m_probeTag; }
94 12 QString &lastProbeReason() { return m_lastProbeReason; }
95 2868 QTimer *idleRenewTimer() const { return m_idleRenewTimer; }
96 5229 QTimer *livenessProbeWatchdog() const { return m_livenessProbeWatchdog; }
97 5229 QTimer *idleRenewWatchdog() const { return m_idleRenewWatchdog; }
98
99 // T-720: Liveness probe budget: the ceiling, and the value until the
100 // first round trip of a session was measured (S92.5.3).
101 static constexpr int PROBE_WATCHDOG_MS = 15000;
102 // S92.5.3: floor and RTT factor of the measured budget. 10 x SRTT leaves
103 // an order of magnitude for server jitter; 3 s covers a 300 ms RTT link.
104 static constexpr int PROBE_WATCHDOG_MIN_MS = 3000;
105 static constexpr int PROBE_WATCHDOG_RTT_FACTOR = 10;
106 static constexpr int IDLE_RENEW_MS = 25 * 60 * 1000; // T-271: 25 min (was 28)
107
108 private:
109 // T-720: Send a probe NOOP and remember its tag. Does not enqueue —
110 // call only in a state that allows commands (Authenticated/Selected).
111 void sendProbeNoop();
112 28 void setState(State state) { m_deps.setState(state); }
113 9 void failConnection(const QString &error,
114 DisconnectReason reason = DisconnectReason::ProtocolError) {
115 9 m_deps.failConnection(error, reason);
116 9 }
117 97 bool hasIdleCapability() const { return m_deps.hasIdleCapability(); }
118 8 bool hasNotifyCapability() const { return m_deps.hasNotifyCapability(); }
119 28 void reportE2eTransportPhase(const QString &phase, quint64 requestToken = 0) {
120 28 m_deps.reportE2eTransportPhase(phase, requestToken);
121 28 }
122
123 QSslSocket *m_socket;
124 ImapCommandTable &m_commands;
125 const State &m_state; // ImapService::m_state
126 Deps m_deps;
127
128 QTimer *m_idleRenewTimer = nullptr;
129 // T-720: Liveness-probe watchdogs (single-shot). PROBE_WATCHDOG_MS is the
130 // budget for a probe NOOP or an IDLE DONE/OK round-trip; it is shorter
131 // than the command timeout so the monitor can claim a ~30 s worst-case
132 // detection window.
133 QTimer *m_livenessProbeWatchdog = nullptr;
134 QTimer *m_idleRenewWatchdog = nullptr;
135 std::optional<double> m_smoothedRttMs; // S92.5.3
136 QElapsedTimer m_doneSent; // DONE written, IDLE OK pending
137 QString m_probeTag; // T-720: tag of the in-flight probe NOOP
138 QString m_lastProbeReason; // T-720: reason string for diagnostic logging
139
140 bool m_autoIdle = true; // T-205: false for body-fetch connections
141 bool m_allowNotifyPush = false;
142
143 // IDLE state
144 bool m_isIdling = false;
145 bool m_idleStartPending = false;
146 QString m_idleTag; // Tag of the IDLE command (for matching tagged OK)
147
148 // T-320: NOTIFY state (RFC 5465)
149 bool m_isNotifying = false;
150 QString m_notifyTag; // Tag of the NOTIFY SET command
151 QStringList m_notifyFolders; // Folders being watched via NOTIFY
152 };
153