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: 78.6% 44 / 0 / 56
Functions: 87.5% 7 / 0 / 8
Branches: 47.0% 31 / 0 / 66

presentation/QuickComposeBackendDrafts.cpp
Line Branch Exec Source
1 // Sprint 108 (S108.7.1, D-21): the composer sessions' copies in the Drafts
2 // folder — each session's newest journaled APPEND replaces its previous
3 // revision (exact UID EXPUNGE through ServerDraftCleanup), a sent or
4 // discarded session leaves none behind.
5 #include "QuickComposeBackend.h"
6 #include "ComposeLog.h"
7 #include "ComposeSession.h"
8
9 #include "app/core/MailSessionCore.h"
10 #include "controller/MailController.h"
11 #include "service/ImapService.h"
12
13 // The Drafts copies on the IMAP connection: UIDs and UIDVALIDITY of the
14 // completed appends, and the cleanup state across reconnects (formerly part
15 // of connectSendAndDrafts, same constructor position).
16 795 void QuickComposeBackend::connectServerDrafts() {
17 795 MailSessionCore &session = m_session;
18 795 connectDraftAppendResults();
19
1/1
✓ Branch 4 → 5 taken 795 times.
795 connect(session.imap(), &ImapService::stateChanged, this, [this](ImapService::State state) {
20
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 9 times.
10 if (state == ImapService::State::Disconnected) m_draftCleanup.reset();
21 10 });
22
1/1
✓ Branch 7 → 8 taken 795 times.
795 connect(session.imap(), &ImapService::appendCompleted, this,
23 795 [this](quint64, const QString &folder, qint64 uid, quint32 uidValidity) {
24 // S106-257: only Drafts copies are ever cleaned up (any APPEND grew the map).
25
3/6
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 11 not taken.
2 if (!m_draftsFolder.isEmpty() && folder == m_draftsFolder) {
26 2 m_draftCleanup.remember(folder, uid, uidValidity);
27 2 m_draftUidValidity.insert(uid, uidValidity); // kept with the session's draft
28 }
29 2 });
30 795 }
31
32 // connectServerDrafts step: completion and failure of the sessions' journaled
33 // Drafts appends.
34 795 void QuickComposeBackend::connectDraftAppendResults() {
35 795 MailSessionCore &session = m_session;
36 // Server-draft bookkeeping: the newest completed append owns the tracked
37 // UID; its cleanup removes the previous revision. Failures keep the local
38 // draft and report explicitly.
39
1/1
✓ Branch 3 → 4 taken 795 times.
795 connect(session.controller(), &MailController::appendMutationCompleted, this,
40 795 [this](qint64 token, const QString &folder, qint64 uid) {
41 // S106-259: superseded while in flight (newer one queued); it would stay for good.
42
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 12 taken 2 times.
2 if (m_supersededDraftTokens.remove(token)) {
43 ✗ if (folder == m_draftsFolder && uid > 0) deleteServerDraftRevision(uid);
44 ✗ return;
45 }
46 // Session revisions: each session tracks its own token.
47
1/1
✓ Branch 12 → 13 taken 2 times.
2 for (auto it = m_sessionDraftTokens.begin();
48
1/2
✓ Branch 44 → 14 taken 2 times.
✗ Branch 44 → 45 not taken.
2 it != m_sessionDraftTokens.end(); ++it) {
49
3/6
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 18 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 2 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 2 times.
2 if (it.value() != token || folder != m_draftsFolder) continue;
50 2 const QString session = it.key();
51
1/1
✓ Branch 25 → 26 taken 2 times.
2 m_sessionDraftTokens.erase(it);
52 2 const qint64 previous = m_sessionDraftUids.value(session, 0);
53
1/1
✓ Branch 27 → 28 taken 2 times.
2 m_sessionDraftUids.insert(session, uid > 0 ? uid : 0);
54 // Sprint 108: the session remembers its copy in the draft file, so
55 // a restart still replaces it instead of adding another one.
56 2 quint32 previousValidity = 0;
57
3/4
✓ Branch 28 → 29 taken 2 times.
✓ Branch 29 → 30 taken 2 times.
✓ Branch 30 → 31 taken 2 times.
✗ Branch 30 → 35 not taken.
2 if (auto *open = qobject_cast<ComposeSession *>(m_sessionStore.session(session))) {
58 2 previousValidity = open->serverDraftUidValidity();
59
1/1
✓ Branch 33 → 34 taken 2 times.
2 open->setServerDraftUid(uid > 0 ? uid : 0, m_draftUidValidity.value(uid, 0));
60 }
61
3/4
✓ Branch 35 → 36 taken 1 time.
✓ Branch 35 → 38 taken 1 time.
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 38 not taken.
2 if (previous > 0 && previous != uid)
62
1/1
✓ Branch 37 → 38 taken 1 time.
1 retireSessionServerCopy(previous, previousValidity);
63 2 return;
64 2 }
65 });
66
1/1
✓ Branch 6 → 7 taken 795 times.
795 connect(session.controller(), &MailController::appendMutationFailed, this,
67 795 [this](qint64 token, const QString &error) {
68 ✗ if (m_supersededDraftTokens.remove(token)) return; // nothing reached the server
69 ✗ for (auto it = m_sessionDraftTokens.begin();
70 ✗ it != m_sessionDraftTokens.end(); ++it)
71 ✗ if (it.value() == token) {
72 ✗ const auto session = it.key();
73 ✗ m_sessionDraftTokens.erase(it);
74 // The local draft keeps everything; the next save tries again.
75 ✗ qCWarning(lcCompose) << "server draft not saved for" << session;
76 ✗ m_deps.setStatus(tr("The draft could not be saved on the server: %1").arg(error));
77 ✗ return;
78 ✗ }
79 });
80 795 }
81
82 22 void QuickComposeBackend::retireSessionServerCopy(qint64 uid, quint32 uidValidity) {
83
5/6
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 21 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
✓ Branch 7 → 8 taken 21 times.
✓ Branch 7 → 9 taken 1 time.
22 if (uid <= 0 || m_draftsFolder.isEmpty()) return;
84
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 11 not taken.
1 if (uidValidity > 0) m_draftCleanup.remember(m_draftsFolder, uid, uidValidity);
85 1 m_draftUidValidity.remove(uid);
86 1 deleteServerDraftRevision(uid);
87 }
88
89 1 void QuickComposeBackend::deleteServerDraftRevision(qint64 draftUid) {
90 1 m_draftCleanup.remove(m_draftsFolder, draftUid);
91 1 }
92
93