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

presentation/QuickContactModel.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QAbstractListModel>
4 #include <QList>
5 #include <QThreadPool>
6 #include <QVariantMap>
7 #include <functional>
8 #include <memory>
9
10 #include "data/ContactQueries.h"
11
12 class ContactStore;
13
14 // Keyed, paged contact list for the Quick frontend (Sprint 97 S97.1.7,
15 // S97.2.2; Q09 before).
16 //
17 // Rows are cards (one per person, D-1) or collected addresses, identified by
18 // key ("<bookId>/<uid>", "collected:<address>") — never by row index
19 // (CB-08). A worker with its own store connection loads keyset pages (100
20 // rows) on demand: the list fetches the next page when it scrolls
21 // (canFetchMore/fetchMore, CD-07). Card changes arrive as keys
22 // (applyCardChanges): loaded rows update in place, move to their new sort
23 // position or disappear, without a model reset — scroll position and
24 // selection survive a background sync (G15). A reset remains for a view or
25 // filter change and for changes of more than half the loaded rows.
26 class QuickContactModel final : public QAbstractListModel {
27 17 Q_OBJECT
28 Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
29 Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged)
30 Q_PROPERTY(bool hasMore READ hasMore NOTIFY totalCountChanged)
31 Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
32 // {kind: "all"|"frequent"|"collected"|"book"|"group", bookId, group}
33 Q_PROPERTY(QVariantMap view READ view WRITE setView NOTIFY viewChanged)
34 Q_PROPERTY(bool sortByFamilyName READ sortByFamilyName WRITE setSortByFamilyName NOTIFY viewChanged)
35 // Bounded recipient suggestions for the compose fields: {name, email,
36 // insert, label, book, collected, initials, avatarColor, key}.
37 Q_PROPERTY(QVariantList completions READ completions NOTIFY completionsChanged)
38 // F100-10: the list's optional columns in their order (ids of
39 // availableColumns()); the name column is always first.
40 Q_PROPERTY(QStringList columns READ columns WRITE setColumns NOTIFY columnsChanged)
41 // F100-13: width of the docked card/editor pane in logical pixels.
42 Q_PROPERTY(int paneWidth READ paneWidth WRITE setPaneWidth NOTIFY paneWidthChanged)
43 public:
44 enum Role {
45 CardKey = Qt::UserRole + 1,
46 DisplayName,
47 PrimaryAddress,
48 AddressCount,
49 Organization,
50 BookName,
51 BookReadOnly,
52 LocalBook,
53 Section,
54 Initials,
55 AvatarColor,
56 HasPhoto,
57 PhotoTag,
58 IsGroup,
59 SyncState,
60 Collected,
61 Writable,
62 BookId,
63 Uid,
64 Phone,
65 JobTitle,
66 Locality,
67 Birthday,
68 Groups,
69 };
70 static constexpr int PageSize = 100;
71
72 explicit QuickContactModel(QString databasePath, QObject *parent = nullptr);
73 ~QuickContactModel() override;
74 QHash<int, QByteArray> roleNames() const override;
75 int rowCount(const QModelIndex &parent = {}) const override;
76 QVariant data(const QModelIndex &index, int role) const override;
77 bool canFetchMore(const QModelIndex &parent) const override;
78 void fetchMore(const QModelIndex &parent) override;
79
80 1186 bool loading() const { return m_loading; }
81 35 int totalCount() const { return m_totalCount; }
82 54 bool hasMore() const { return m_hasMore; }
83 332 QString filter() const { return m_filter; }
84 void setFilter(const QString &filter);
85 QVariantMap view() const;
86 void setView(const QVariantMap &view);
87 16 bool sortByFamilyName() const { return m_view.byFamilyName; }
88 void setSortByFamilyName(bool byFamily);
89 2355 QString databasePath() const { return m_databasePath; }
90 4059 QStringList columns() const { return m_columns; }
91 void setColumns(const QStringList &columns);
92 403 int paneWidth() const { return m_paneWidth; }
93 void setPaneWidth(int width);
94 // S106-354: the occasion of a "how we met" record by its event UID.
95 797 void setEventTitleResolver(std::function<QString(const QString &)> resolver) { m_eventTitle = std::move(resolver); }
96 // Every optional column id in its default order: email, phone,
97 // organization, title, city, birthday, groups, book.
98 Q_INVOKABLE static QStringList availableColumns();
99
100 // Restarts the projection (view/filter unchanged).
101 Q_INVOKABLE void reload();
102 // Kept for callers of the pre-Sprint-97 API; fetchMore is automatic.
103
1/1
✓ Branch 3 → 4 taken 2 times.
2 Q_INVOKABLE void loadMore() { fetchMore({}); }
104 // Row of a key in the loaded rows, -1 when not loaded.
105 Q_INVOKABLE int indexOfKey(const QString &key) const;
106 // Key of a loaded row ("" outside), for range selection over rows the
107 // view has not instantiated.
108 Q_INVOKABLE QString keyAt(int row) const;
109 // The full card of a key for the card pane and the editor
110 // (ContactEditorFields map plus book, sync state, writable); {} if gone.
111 Q_INVOKABLE QVariantMap card(const QString &key) const;
112 // Per-card updates after a write or a sync.
113 void applyCardChanges(const QStringList &keys);
114 Q_INVOKABLE void requestCompletions(const QString &fragment);
115 645 QVariantList completions() const { return m_completions; }
116
117 signals:
118 void loadingChanged();
119 void totalCountChanged();
120 void filterChanged();
121 void viewChanged();
122 void completionsChanged();
123 void columnsChanged();
124 void paneWidthChanged();
125 // A changed card moved to another row (the view keeps it selected).
126 void cardMoved(const QString &key, int row);
127
128 private:
129 void restartProjection();
130 void loadPage();
131 bool ensureReader() const;
132 int insertPosition(const ContactQueries::Row &row) const;
133 void refreshCount();
134 QVariant rowData(const ContactQueries::Row &row, int role) const;
135
136 QString m_databasePath;
137 QString m_filter;
138 ContactQueries::View m_view;
139 QList<ContactQueries::Row> m_rows;
140 // GUI-thread read connection for per-card updates and card(); the paged
141 // reads use isolated worker connections.
142 mutable std::unique_ptr<ContactStore> m_reader;
143 std::function<QString(const QString &)> m_eventTitle;
144 QThreadPool m_pool;
145 // P90.13: latest-wins lane for recipient completion.
146 QThreadPool m_completionPool;
147 int m_totalCount = 0;
148 bool m_hasMore = false;
149 bool m_loading = false;
150 quint64 m_generation = 0;
151 QVariantList m_completions;
152 quint64 m_completionGeneration = 0;
153 QStringList m_columns;
154 int m_paneWidth = 440;
155 };
156