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

data/MutationJournalStore.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QByteArray>
4 #include <QList>
5 #include <QSet>
6 #include <QSqlDatabase>
7 #include <QString>
8 #include <functional>
9 #include <optional>
10
11 #include "data/MailRequest.h"
12 #include "data/MutationJournal.h"
13
14 // Durable mutation journal of the mail cache (Sprint 91 S91.3.2, extracted
15 // from MailCache): local intent → durable → dispatched → confirmed/failed.
16 // Callers reach it as `cache.journal()`.
17 //
18 // Plain class, no connection of its own: it uses the owning MailCache's
19 // QSqlDatabase and lastError string on that cache's thread (writer workers
20 // open their own MailCache). Every writer runs inside a ScopedWriteUnit: it
21 // joins a transaction the caller already holds (MailCache's flag/keyword
22 // writers, the selection move) and owns BEGIN IMMEDIATE otherwise, so the
23 // journal and its data change stay atomic (rule 6).
24 // enqueueMutationsInTransaction() never starts a transaction. Phase changes
25 // and removals are announced through notifiers bound to MailCache's
26 // mutationPhaseChanged / mutationRemoved signals.
27 class MutationJournalStore {
28 public:
29 struct Notify {
30 std::function<void(const QString &account, qint64 folderId, qint64 token,
31 qint64 uid, bool pending)> mutationPhaseChanged;
32 std::function<void(qint64 token)> mutationRemoved;
33 };
34 MutationJournalStore(QSqlDatabase &db, QString &lastError, Notify notify);
35
36 // Persist before optimistic UI state changes. Desired-state operations for
37 // the same full identity + flag are atomically coalesced to the last intent.
38 // Returns the durable token, or nullopt when validation/backpressure/storage
39 // rejects the operation.
40 std::optional<qint64> enqueueMutation(const MutationRecord &record);
41 struct AppendPayload {
42 QByteArray message;
43 QString flags;
44 };
45 static constexpr qint64 MaximumAppendMessageBytes = 80LL * 1024 * 1024;
46 static constexpr qint64 MaximumPendingAppendBytes = 256LL * 1024 * 1024;
47 // Journal intent and exact MIME bytes commit together. Confirmation/removal
48 // cascades to the payload; failed or ambiguous copies retain recovery data.
49 // A trusted builder may supply the SHA-256 of these exact immutable bytes
50 // so an already prepared payload need not be hashed on the GUI thread.
51 std::optional<qint64> enqueueAppendMutation(const MutationRecord &record,
52 const QByteArray &message, const QString &flags,
53 const QByteArray &precomputedSha256 = {});
54 std::optional<AppendPayload> appendPayload(qint64 token, const QString &accountId,
55 QString *readError = nullptr) const;
56 qint64 appendPayloadBytes() const;
57 std::optional<QList<MutationRecord>> pendingAppendMutations(const QString &accountId, int limit = 100) const;
58 bool requeueFailedAppend(qint64 token, const QString &accountId);
59 // Persist one complete user action with a single capacity check and commit.
60 // The whole batch is rejected atomically; returned tokens retain input order.
61 std::optional<QList<qint64>>
62 enqueueMutations(const QList<MutationRecord> &records);
63 // Atomically reserve the exact queued intent before passing it to transport.
64 // A successful queued cancellation and a successful claim are exclusive.
65 bool claimMutationForDispatch(const MutationRecord &expected);
66 QList<MutationRecord> pendingMutations(const QString &accountId = {}) const;
67 std::optional<MutationRecord> mutation(qint64 token) const;
68 bool updateMutationPhase(qint64 token, MutationPhase phase,
69 const QString &error = {},
70 const QString &uidPlusEvidence = {});
71 // Removes the entry and announces it (mutationRemoved). A caller inside
72 // its own transaction passes `deferred`: the token is collected there and
73 // announced with announceRemoved() after the commit (S106-116), so a
74 // rolled-back removal is never published.
75 bool removeMutation(qint64 token, QList<qint64> *deferred = nullptr);
76 2839 void announceRemoved(qint64 token) const { m_notify.mutationRemoved(token); }
77 // After a confirmed MOVE or authoritative absence of a removed source:
78 // retire its obsolete flags, preserving other identities/operation kinds.
79 QList<qint64> retireSourceFlagMutations(const MailId &source, quint32 uidValidity);
80 // 0 when the journal cannot be read; capacity checks use journalUsage().
81 int mutationCount() const;
82 qint64 mutationJournalBytes() const;
83 // Entries and estimated bytes in one query; false when unreadable.
84 bool journalUsage(int *count, qint64 *bytes) const;
85 // P90.16: UIDs with an unresolved journaled move out of this folder.
86 // Projection loads exclude them so pending moves never re-appear in the
87 // visible list while the server confirmation is still outstanding.
88 QSet<qint64> pendingMoveUids(const QString &account, qint64 folderId,
89 bool *ok = nullptr) const;
90 bool hasPendingSeenMutation(const QString &account, qint64 folderId) const;
91 // Caller owns BEGIN IMMEDIATE, commit and rollback.
92 std::optional<QList<qint64>> enqueueMutationsInTransaction(
93 const QList<MutationRecord> &records, bool *capacityExceeded = nullptr);
94
95 // Backpressure limits (tests configure small journals through them).
96 155 int maxEntries() const { return m_mutationJournalMaxEntries; }
97 6566 qint64 maxBytes() const { return m_mutationJournalMaxBytes; }
98 8 void setMaxEntries(int entries) { m_mutationJournalMaxEntries = entries; }
99 5 void setMaxBytes(qint64 bytes) { m_mutationJournalMaxBytes = bytes; }
100
101 private:
102 QSqlDatabase &m_db; // owned by MailCache
103 QString &m_lastError; // MailCache::lastError()
104 Notify m_notify;
105 int m_mutationJournalMaxEntries = 5000;
106 qint64 m_mutationJournalMaxBytes = 2LL * 1024 * 1024;
107 };
108