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% 17 / 0 / 17
Functions: 100.0% 11 / 0 / 11
Branches: 70.0% 14 / 0 / 20

data/Models.h
Line Branch Exec Source
1 #pragma once
2 #include <optional>
3
4 #include <QDateTime>
5 #include <QString>
6 #include <QStringList>
7
8 // FolderInfo represents a single IMAP mailbox from the LIST response.
9 // Used to populate the FolderTree UI.
10 struct FolderInfo {
11 QString name; // Display name, UTF-8 decoded (e.g. "Entwürfe")
12 QString path; // Raw IMAP path (e.g. "INBOX.Entw&APw-rfe")
13 QStringList flags; // IMAP flags: \HasChildren, \Noselect, \Sent, etc.
14 QString delimiter; // Hierarchy delimiter: "." or "/"
15
16 // Convenience: check for common flags
17 1 bool hasChildren() const {
18
1/1
✓ Branch 2 → 3 taken 1 time.
1 return flags.contains("\\HasChildren", Qt::CaseInsensitive);
19 }
20 24746 bool isNoselect() const {
21
4/7
✓ Branch 2 → 3 taken 24746 times.
✓ Branch 4 → 5 taken 24651 times.
✓ Branch 4 → 8 taken 95 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 24651 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
74143 return flags.contains("\\Noselect", Qt::CaseInsensitive) ||
22
4/7
✓ Branch 5 → 6 taken 24651 times.
✓ Branch 10 → 11 taken 24651 times.
✓ Branch 10 → 12 taken 95 times.
✓ Branch 12 → 13 taken 24746 times.
✗ Branch 12 → 14 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
74143 flags.contains("\\NonExistent", Qt::CaseInsensitive);
23 }
24
1/1
✓ Branch 2 → 3 taken 9285 times.
9285 bool isSent() const { return flags.contains("\\Sent", Qt::CaseInsensitive); }
25 9273 bool isDrafts() const {
26
1/1
✓ Branch 2 → 3 taken 9273 times.
9273 return flags.contains("\\Drafts", Qt::CaseInsensitive);
27 }
28 9276 bool isTrash() const {
29
1/1
✓ Branch 2 → 3 taken 9276 times.
9276 return flags.contains("\\Trash", Qt::CaseInsensitive);
30 }
31 9220 bool isArchive() const {
32
1/1
✓ Branch 2 → 3 taken 9220 times.
9220 return flags.contains("\\Archive", Qt::CaseInsensitive);
33 }
34
1/1
✓ Branch 2 → 3 taken 9226 times.
9226 bool isJunk() const { return flags.contains("\\Junk", Qt::CaseInsensitive); }
35 };
36
37 // Flag bitmask constants for MailHeader::flags.
38 // Values match IMAP system flag semantics.
39 namespace MailFlag {
40 constexpr quint32 None = 0;
41 constexpr quint32 Seen = 1 << 0; // \Seen
42 constexpr quint32 Answered = 1 << 1; // \Answered
43 constexpr quint32 Flagged = 1 << 2; // \Flagged
44 constexpr quint32 Deleted = 1 << 3; // \Deleted
45 constexpr quint32 Draft = 1 << 4; // \Draft
46 } // namespace MailFlag
47
48 // MailHeader represents the envelope data of a single email.
49 // Stored in SQLite cache for offline access and fast listing.
50 struct MailHeader {
51 qint64 uid = 0; // IMAP UID (unique within a folder's UIDVALIDITY)
52 qint64 folderId = 0; // FK to MailCache folders table
53 QString subject;
54 QString from;
55 QString to; // every To address, "Name <a@b>, c@d" (Sprint 95)
56 QString cc; // every Cc address, same form (Sprint 95)
57 QString listId; // List-Id without <> and display name; empty = none
58 // True when to/cc/listId come from a sync since cache schema v24; rows
59 // synced earlier have only the first To address and no Cc/List-Id.
60 bool recipientsKnown = false;
61 QDateTime date;
62 quint32 flags = MailFlag::None; // Bitmask of MailFlag constants
63 qint64 size = 0; // RFC822.SIZE in bytes
64 QStringList labels; // IMAP keywords (non-system flags)
65 bool hasAttachments = false; // Cached: has body attachments?
66 bool isSpam = false; // T-231: X-Spam: Yes header detected
67 // Sprint 105 S105.2.2: the server filter's score as stored at header sync
68 // (spam_score/spam_threshold/spam_tool, schema v25). spamKnown = this
69 // writer read the filter fields; false keeps what a sync stored before.
70 bool spamKnown = false;
71 std::optional<double> spamScore;
72 std::optional<double> spamThreshold;
73 QString spamTool; // "SpamAssassin", "Rspamd" or "" (none)
74 QString messageId; // Message-ID header (without <>)
75 QString inReplyTo; // First In-Reply-To ID (compatibility view)
76 QStringList inReplyToIds; // Ordered canonical In-Reply-To token list
77 QStringList references; // References header (list of message-ids)
78
79 // Flag convenience methods
80 381009 bool isSeen() const { return flags & MailFlag::Seen; }
81 53470 bool isAnswered() const { return flags & MailFlag::Answered; }
82 55563 bool isFlagged() const { return flags & MailFlag::Flagged; }
83 3 bool isDraft() const { return flags & MailFlag::Draft; }
84 };
85
86 struct MailIdentity {
87 QString account;
88 qint64 folderId = -1;
89 qint64 uid = -1;
90
91 bool isValid() const { return folderId > 0 && uid >= 0; }
92 };
93
94 // Attachment metadata for a single email attachment.
95 // BLOB data is loaded on-demand from MailCache (lazy loading).
96 enum class AttachmentPresentation {
97 Regular = 0, // Visible downloadable attachment
98 InlineCandidate = 1, // Eligible for a sanitized cid: image
99 ExplicitAttachmentWithContentId = 2, // CID-bearing but still downloadable
100 RejectedInline = 3, // Unsafe, ambiguous, or over budget
101 };
102
103 struct Attachment {
104 qint64 id = 0; // DB primary key (0 = not yet stored)
105 QString filename; // e.g. "report.pdf"
106 QString contentType; // e.g. "application/pdf"
107 qint64 size = 0; // Decoded size in bytes
108 QString contentId; // Content-ID for inline attachments (without <>)
109 AttachmentPresentation presentation = AttachmentPresentation::Regular;
110 };
111
112 // MailBody holds the content of a single email.
113 // textHtml contains decoded HTML if the message had a text/html part.
114 struct MailBody {
115 qint64 uid = 0;
116 QString textPlain; // Decoded text/plain body (UTF-8)
117 QString textHtml; // Decoded text/html body (UTF-8), empty if none
118 QByteArray rawSource; // Original raw MIME source (headers + body)
119 QList<Attachment> attachments; // Attachment metadata (no BLOB data)
120 quint32 cacheUidValidity = 0; // Captured with the cached display snapshot
121 QString cacheAccountId;
122 // P90.8: which variants this body actually carries. A mode projection can
123 // leave one variant legitimately empty ("not loaded") even though the
124 // store has it — the reader must then ask for a reprojection instead of
125 // treating the mail as html-less/plain-less. Full means both variants
126 // were read (or written), so emptiness is authoritative. Stays last so
127 // positional aggregate initialization keeps compiling.
128 enum class Projection { Full, TextOnly, HtmlOnly };
129 Projection projection = Projection::Full;
130 // Digest of the decoded HTML variant, computed on the read/parse worker
131 // when it fits the native document budget. Empty means unavailable.
132 QByteArray htmlDigest;
133 // F100-56: the spam filter's header lines ("x-spam-status: Yes, score=…"),
134 // extracted on the display read worker (util/SpamHeaders.h). Empty = none.
135 QString spamHeaders;
136 };
137
138 // Result of an IMAP STATUS command (MESSAGES, UNSEEN, RECENT counts).
139 struct StatusResult {
140 QString folderPath;
141 int messages = 0;
142 int unseen = 0;
143 int recent = 0;
144 // Local steady-clock origin of a successfully completed explicit STATUS.
145 // Zero identifies an unsolicited notification with no causal query boundary.
146 qint64 requestStartedNs = 0;
147 bool hasMessages = true;
148 bool hasUnseen = true;
149 };
150
151 // Authoritative response to UID FETCH ... FLAGS. Unlike the legacy bitmask
152 // pair this retains IMAP keywords so durable label mutations can be verified.
153 struct ImapFlagSnapshot {
154 qint64 uid = 0;
155 quint32 flags = MailFlag::None;
156 QStringList keywords;
157 };
158
159 // T-154: Contact entry for the local contact store.
160 // Used for autocomplete, @-mentions, and CardDAV sync.
161 struct Contact {
162 qint64 id = 0; // DB primary key
163 QString displayName; // "Max Mustermann"
164 QString email; // "max@example.com"
165 QString source; // "local" or "carddav"
166 QString cardDavUid; // CardDAV row identity (empty for local contacts)
167 QString cardDavEtag; // CardDAV ETag for sync
168 QString cardDavHref; // CardDAV resource URL for write-back (Q09)
169 QString cardDavVUid; // Original vCard UID property (write-back)
170 QString cardDavAccountId; // Owning DAV account (stable collection identity)
171 QString cardDavBookPath; // Owning address-book collection path
172 QDateTime lastUsed; // Last time used as recipient
173 int useCount = 0; // How often used as recipient
174 QDateTime createdAt;
175 QDateTime updatedAt;
176 };
177