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

controller/FolderFlagSync.h
Line Branch Exec Source
1 #pragma once
2
3 #include "controller/FolderContext.h"
4 #include "controller/PendingFlagIntents.h"
5 #include <QElapsedTimer>
6 #include <QHash>
7 #include <QList>
8 #include <QObject>
9 #include <QPair>
10 #include <QStringList>
11 #include <QThreadPool>
12 #include <atomic>
13 #include <functional>
14 #include <memory>
15 #include <optional>
16
17 class ImapService;
18 class MailCache;
19 class MailListModel;
20 class MailThreadModel;
21 class QTimer;
22 struct StatusResult;
23
24 // Live flag and count synchronisation of the selected folder (Sprint 91
25 // S91.2.6, extracted from MailController): IDLE pushes, FETCH FLAGS /
26 // CONDSTORE snapshot reconciliation (small folders on the GUI thread, large
27 // ones on a read-only worker), STATUS polling of the subscribed folders and
28 // the IDLE/NOTIFY restart that ends a folder open. Callers reach it as
29 // `controller.flagSync()`.
30 //
31 // Owner: MailController (value member). GUI thread; the membership diff
32 // runs on the unit's two-thread pool with its own read-only cache
33 // connection. MailController connects the main connection's IDLE, flag and
34 // STATUS signals (and the search connection's STATUS signal) to the unit
35 // at the former positions, so receiver order is unchanged. The folder
36 // context, pending intents, subscribed folders and header-stream state stay
37 // owned by the controller (references / Deps); finishFlagSync — which
38 // continues the header-stream sequence — stays in the controller and is
39 // called through Deps. Controller signals keep their emitter (notifiers).
40 class FolderFlagSync final : public QObject {
41 Q_OBJECT
42 public:
43 struct Deps {
44 std::function<void()> ensureSearchConnection;
45 std::function<bool()> beginHeaderStream;
46 std::function<int()> pendingHeaderWrites;
47 std::function<void(const QString &)> failHeaderWrite;
48 // Keep the newest complete flag snapshot until the header writes settle.
49 std::function<void(const QList<QPair<qint64, quint32>> &)> deferHeaderFlags;
50 std::function<void()> resyncCurrentFolder;
51 std::function<void(QList<qint64>, bool)> finishFlagSync;
52 std::function<void()> requestFolderCountStatus;
53 // MailController signals (the emitter stays the controller).
54 std::function<void(const QString &)> statusMessage;
55 std::function<void(const QString &, int)> unreadCountChanged;
56 std::function<void(const QString &, int)> cachedUnreadCountChanged;
57 std::function<void(qint64, int, quint64)> authoritativeFolderPresented;
58 };
59 FolderFlagSync(ImapService *imap, ImapService *const &searchImap, MailCache *cache,
60 MailListModel *model, MailThreadModel *const &threadModel,
61 const FolderContext &folder, PendingFlagIntents &flagIntents,
62 const QStringList &subscribedFolders, const bool &folderPresentationEnabled,
63 QElapsedTimer &folderSwitchStopwatch, Deps deps, QObject *parent = nullptr);
64 // Polling and expunge-debounce timers; called at the end of the
65 // controller constructor (their former position).
66 void connectTimers();
67
68 // Quick's published indexed page can supply visible flags when the hidden
69 // flat model has no copy of those rows. Called only on the GUI thread.
70 795 void setVisibleIndexedFlagsProvider(
71 std::function<QHash<qint64, quint32>(qint64)> provider) {
72 795 m_visibleIndexedFlags = std::move(provider);
73 795 }
74 // Sprint 49: Trigger immediate poll of all subscribed folders
75 void triggerPollNow();
76 // Folder open finished its sync: NOTIFY or IDLE (plus STATUS polling).
77 void startIdleIfPossible();
78
79 // Controller state transitions (folder/account switch, teardown).
80 void cancelMembershipRead(); // new sequence, cancel the running diff
81 void abortMembershipRead(); // cancel only (destructor)
82 void waitForMembershipReads();
83 void stopPolling();
84 void restartPolling(); // stop, then start when folders are subscribed
85 void resetPollingIndex();
86 void clearChangedSinceFlags();
87
88 // Observable state (tests and diagnostics).
89 bool expungeRefreshPending() const;
90 bool pollingActive() const;
91 2 bool initialPollDone() const { return m_initialPollDone; }
92 3 int pollingIndex() const { return m_pollingIndex; }
93 4 void setPollingIndex(int index) { m_pollingIndex = index; }
94
95 public slots:
96 // IDLE event handlers
97 void onIdleNewMessages(int newCount);
98 void onIdleFlagsChanged(qint64 uid, quint32 flags);
99 void onIdleFlagsNeedRefetch(int seqNo);
100 void onIdleMessageExpunged(int seqNo);
101 // Flag sync result
102 void onFlagsReceived(const QList<QPair<qint64, quint32>> &uidFlags);
103 void onChangedFlagsReceived(const QList<QPair<qint64, quint32>> &uidFlags);
104 void onUidMembershipReceived(const QList<qint64> &uids);
105 // Folder status (polling) result
106 void onFolderStatusReceived(const StatusResult &result);
107 // F-26: a failed STATUS keeps the round going (failure isolation).
108 void onFolderStatusFailed(const QString &folder);
109 // Polling timer
110 void pollFolders();
111 void pollNextFolder();
112
113 private:
114 // `membership` set: CONDSTORE reopen (uidFlags holds only changed mail).
115 void reconcileLargeFlagSnapshot(const QList<QPair<qint64, quint32>> &uidFlags,
116 6 std::optional<QList<qint64>> membership = std::nullopt);
117 bool idlePushBelongsToCurrentFolder() const;
118
119 ImapService *m_imap;
120 ImapService *const &m_searchImap; // owned by MailController (lazy)
121 MailCache *m_cache;
122 MailListModel *m_model;
123 MailThreadModel *const &m_threadModel; // set later on MailController
124 const FolderContext &m_folder; // MailController::m_folder
125 PendingFlagIntents &m_flagIntents; // MailMutationPipeline::intents()
126 const QStringList &m_subscribedFolders; // MailController::m_subscribedFolders
127 const bool &m_folderPresentationEnabled;
128 QElapsedTimer &m_folderSwitchStopwatch; // started by the folder switch
129 Deps m_deps;
130 QTimer *m_pollingTimer = nullptr;
131 int m_pollingIndex = 0;
132 bool m_initialPollDone = false; // T-066: initial poll on first IDLE start
133 // Debounce timer for IDLE expunge events (avoid N× fetchFlags for bulk deletes)
134 QTimer *m_expungeDebounceTimer = nullptr;
135 QThreadPool m_flagMembershipPool;
136 std::shared_ptr<std::atomic_bool> m_flagMembershipCancelled;
137 quint64 m_flagMembershipSequence = 0;
138 std::function<QHash<qint64, quint32>(qint64)> m_visibleIndexedFlags;
139 QList<QPair<qint64, quint32>> m_changedSinceFlags;
140 };
141