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: 87.5% 7 / 0 / 8
Functions: 87.5% 7 / 0 / 8
Branches: -% 0 / 0 / 0

presentation/QuickContactMailHistory.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QObject>
4 #include <QStringList>
5 #include <QThreadPool>
6 #include <QHash>
7 #include <QPair>
8 #include <QVariantList>
9 #include <functional>
10
11 // "Recent mails" of the contact card (Sprint 97 S97.5.3, CJ9): the last
12 // five mails from or to any of a person's addresses, newest first, read
13 // from the local search index on a worker (MailSearchReadService::readCache,
14 // one bounded page per address and direction). Latest wins: a newer
15 // request makes an older answer irrelevant (generation fence).
16 //
17 // "To" covers every To and Cc address since cache schema v24 (Sprint 95
18 // S95.1.11); mails synced before that know only their first To address
19 // until they are fetched again.
20 //
21 // Sprint 103 S103.6.2/S103.6.4 (I-9, interaction.md §2.5): requestPerson()
22 // builds the card's history — mails with the person, mails linked to the
23 // card (D-15), events that name the person or invite their addresses, and
24 // the first meeting — upcoming events first, then the past newest first;
25 // "Last seen: <date> · <event>" for an event in the past 365 days. Edits of
26 // the card are never listed.
27 //
28 // Sprint 105 Phase 5 (D-7): mails come from the cache's address index in one
29 // query (MailAddressHistory) on a connection the worker keeps open, with
30 // the old per-address search only while the background backfill runs; the
31 // calendar store stays open on the worker too. The last 64 histories are
32 // kept (stamped with the mail and calendar data_version): a card opens with
33 // its cached items in the same frame and refreshes in the background only
34 // when a stamp moved.
35 class QuickContactMailHistory final : public QObject {
36 172 Q_OBJECT
37 // [{subject, from, dateText, folder, messageId, sent}] — at most Limit.
38 Q_PROPERTY(QVariantList mails READ mails NOTIFY mailsChanged)
39 Q_PROPERTY(bool loading READ loading NOTIFY mailsChanged)
40 // [{kind ("mail", "linked", "event", "met"), dateText, title, detail,
41 // messageId, upcoming, event: {uid, accountId, calendarPath, startIso}}]
42 Q_PROPERTY(QVariantList items READ items NOTIFY mailsChanged)
43 Q_PROPERTY(QString lastSeen READ lastSeen NOTIFY mailsChanged)
44 public:
45 static constexpr int Limit = 5;
46 static constexpr int MaxAddresses = 4;
47 using DatabasePath = std::function<QString()>;
48 explicit QuickContactMailHistory(DatabasePath databasePath, QObject *parent = nullptr);
49 ~QuickContactMailHistory() override;
50
51 ✗ QVariantList mails() const { return m_mails; }
52 1607 QVariantList items() const { return m_items; }
53 453 QString lastSeen() const { return m_lastSeen; }
54 795 void setCalendarPath(const QString &path) { m_calendarPath = path; }
55 // person: {addresses, uid, met: {date (ISO), text}, links: [{messageId, subject, date}]};
56 // `limit` 5 on the card, 50 after *Show more*.
57 Q_INVOKABLE void requestPerson(const QVariantMap &person, int limit);
58 // S105.5.5: read a neighbour card's history into the cache (nothing shown).
59 Q_INVOKABLE void prefetchPerson(const QVariantMap &person, int limit);
60 struct History {
61 QVariantList items;
62 QString lastSeen;
63 qint64 mailVersion = -1; // stamps of the data the history was read from
64 qint64 calendarVersion = -1;
65 };
66 static constexpr int CachedHistories = 64;
67 // The worker's stamps for `mailDatabase`/`calendarDatabase` (data_version).
68 static QPair<qint64, qint64> stamps(const QString &mailDatabase, const QString &calendarDatabase);
69 // Tests: how many histories are cached, and whether the last request was
70 // answered from the cache in the same frame.
71 1 int cachedHistoryCount() const { return int(m_cache.size()); }
72 1 bool lastRequestFromCache() const { return m_lastFromCache; }
73 // S105.5.2: one backfill chunk of the address index (worker-safe); true
74 // when the index is complete.
75 static bool backfillAddressIndex(const QString &mailDatabase, int chunk = 5000);
76 // S105.5.2: start the background backfill (chunks at idle pace).
77 void startAddressBackfill();
78 // Worker-safe.
79 static History readHistory(const QString &mailDatabase, const QString &calendarDatabase, const QVariantMap &person,
80 int limit);
81 866 bool loading() const { return m_loading; }
82 Q_INVOKABLE void request(const QStringList &addresses);
83 Q_INVOKABLE void clear();
84 // Worker-safe: the merged, newest-first list for these addresses.
85 static QVariantList read(const QString &databasePath, const QStringList &addresses, int limit = Limit);
86 // The pre-index path (one LIKE search per address and direction); the
87 // fallback while the index is incomplete and the golden reference.
88 static QVariantList readByLike(const QString &databasePath, const QStringList &addresses, int limit = Limit);
89
90 signals:
91 void mailsChanged();
92
93 private:
94 DatabasePath m_databasePath;
95 QThreadPool m_pool;
96 QVariantList m_mails;
97 QVariantList m_items;
98 QString m_lastSeen;
99 QString m_calendarPath;
100 bool m_loading = false;
101 quint64 m_generation = 0;
102 struct CacheEntry {
103 History history;
104 };
105 QHash<QString, CacheEntry> m_cache;
106 QStringList m_cacheOrder; // least recently used first
107 bool m_lastFromCache = false;
108 bool m_backfillRunning = false;
109 void remember(const QString &key, const History &history);
110 QString cacheKey(const QVariantMap &person, int limit) const;
111 void scheduleBackfill(int delayMs);
112 };
113