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: 70.0% 7 / 0 / 10
Functions: 70.0% 7 / 0 / 10
Branches: -% 0 / 0 / 0

presentation/SearchTimelineModel.h
Line Branch Exec Source
1 #pragma once
2
3 #include "app/TimeHistogram.h"
4 #include <QAbstractListModel>
5 #include <QDate>
6 #include <QLocale>
7 #include <QMap>
8 #include <QPointer>
9 #include <QVariantList>
10 #include <QVector>
11
12 class MailSearchSession;
13
14 // Sprint 101 S101.5.2–S101.5.8, S101.5.11 (D-7, D-8, D-9, SD-04, SD-10,
15 // SD-11): the bars, axis labels and breadcrumb of a search tab's timeline,
16 // from the session's day counts through TimeHistogram. One row per bucket
17 // (empty buckets included, SD-04). Every interaction ends in one call on
18 // the session (setDateRange), so the date lives in the query alone (rule 8).
19 //
20 // Drill is instant (S101.5.4): when the query's date range narrows, the
21 // bars are re-bucketed from the day counts already known until the
22 // summary of the new query replaces them; `stale` is true meanwhile.
23 //
24 // Owner: MailSearchSessions (one, bound to the active tab's session); QML
25 // reads it as searchTabs.timeline and sets plotWidth. GUI thread only.
26 class SearchTimelineModel final : public QAbstractListModel {
27 11167 Q_OBJECT
28 Q_PROPERTY(MailSearchSession *session READ session WRITE setSession NOTIFY sessionChanged)
29 Q_PROPERTY(qreal plotWidth READ plotWidth WRITE setPlotWidth NOTIFY timelineChanged)
30 Q_PROPERTY(QString unit READ unitName NOTIFY timelineChanged)
31 Q_PROPERTY(qint64 maxCount READ maxCount NOTIFY timelineChanged)
32 Q_PROPERTY(QVariantList levels READ levels NOTIFY timelineChanged)
33 Q_PROPERTY(QString rangeLabel READ rangeLabel NOTIFY timelineChanged)
34 Q_PROPERTY(bool canStep READ canStep NOTIFY timelineChanged)
35 Q_PROPERTY(bool canZoomOut READ canZoomOut NOTIFY timelineChanged)
36 Q_PROPERTY(bool stale READ stale NOTIFY timelineChanged)
37 Q_PROPERTY(bool hasData READ hasData NOTIFY timelineChanged)
38 Q_PROPERTY(int caretBucket READ caretBucket NOTIFY caretChanged)
39 Q_PROPERTY(int viewportFirst READ viewportFirst NOTIFY viewportChanged)
40 Q_PROPERTY(int viewportLast READ viewportLast NOTIFY viewportChanged)
41
42 public:
43 enum Role { FromRole = Qt::UserRole + 1, ToRole, CountRole, LabelRole, AccessibleRole, FractionRole, HourRole };
44
45 explicit SearchTimelineModel(QObject *parent = nullptr);
46
47 int rowCount(const QModelIndex &parent = {}) const override;
48 QVariant data(const QModelIndex &index, int role) const override;
49 QHash<int, QByteArray> roleNames() const override;
50
51 ✗ MailSearchSession *session() const { return m_session; }
52 void setSession(MailSearchSession *session);
53 ✗ qreal plotWidth() const { return m_plotWidth; }
54 void setPlotWidth(qreal width);
55 QString unitName() const;
56 ✗ qint64 maxCount() const { return m_maxCount; }
57 QVariantList levels() const;
58 QString rangeLabel() const;
59 bool canStep() const;
60 1 bool canZoomOut() const { return m_constrained; }
61 594 bool stale() const { return m_stale; }
62 // There are dated hits (or a date range) to show; independent of the
63 // plot width, so a hidden band can be laid out before it has bars.
64 1076 bool hasData() const { return m_range.isValid(); }
65 652 int caretBucket() const { return m_caret; }
66 // S101.5.8: the buckets [first, last] of the rows visible in the list;
67 // -1 when the list shows every result or nothing is visible.
68 214 int viewportFirst() const { return m_viewportFirst; }
69 226 int viewportLast() const { return m_viewportLast; }
70 // The list's visible view rows, first <= last; (-1, -1) hides the band.
71 Q_INVOKABLE void setVisibleRows(int first, int last);
72
73 // Collision-free axis labels {bucket, label} for a slot width; a label
74 // is measured as characters × `charWidth` (the axis font's average).
75 Q_INVOKABLE QVariantList ticks(qreal bucketWidth, qreal charWidth) const;
76 // D-8: a click on bucket `index`. Hours do not filter: the view scrolls
77 // to the hour's first mail (hourRequested).
78 Q_INVOKABLE void drill(int index);
79 Q_INVOKABLE void brush(int first, int last);
80 Q_INVOKABLE void zoomOut();
81 Q_INVOKABLE void zoomToLevel(int level);
82 // ‹ › : the previous/next period of the same length (−1 / +1).
83 Q_INVOKABLE void step(int direction);
84 // S101.5.9: a section of the grouped list ("March 2025", the unit of the
85 // bars): its number of results and the drill into it.
86 Q_INVOKABLE qint64 countForPeriod(const QString &label) const;
87 Q_INVOKABLE void drillPeriod(const QString &label);
88 // The read-out of a bucket in the info line: "142 results · March 2025".
89 Q_INVOKABLE QString tooltip(int index) const;
90
91 // Pure helpers, public for tests.
92 static TimeHistogram::Range steppedRange(const TimeHistogram::Range &range, int direction,
93 Qt::DayOfWeek weekStart);
94
95 signals:
96 void sessionChanged();
97 void timelineChanged();
98 void caretChanged();
99 void viewportChanged();
100 void hourRequested(int hour);
101 // A drill, brush, zoom or step set a range (D-30: ends the first-use hint).
102 void rangeApplied();
103
104 private:
105 void rebuild();
106 void updateCaret();
107 void updateViewport();
108 int bucketFor(const QDateTime &when) const;
109 void applyRange(const TimeHistogram::Range &range);
110 TimeHistogram::Range shownRange() const;
111
112 QPointer<MailSearchSession> m_session;
113 QMetaObject::Connection m_queryConnection;
114 QMetaObject::Connection m_summaryConnection;
115 QMetaObject::Connection m_selectionConnection;
116 QMetaObject::Connection m_rowsConnection;
117 qreal m_plotWidth = 0;
118 QLocale m_locale;
119 Qt::DayOfWeek m_weekStart = Qt::Monday;
120 QMap<QDate, qint64> m_dayCounts; // last summary (or server-updated) day counts
121 QVector<qint64> m_hourCounts;
122 QDate m_firstHit;
123 QDate m_lastHit;
124 TimeHistogram::Range m_range; // shown range
125 TimeHistogram::Unit m_unit = TimeHistogram::Unit::Year;
126 QList<TimeHistogram::Bucket> m_buckets;
127 qint64 m_maxCount = 0;
128 bool m_constrained = false; // the query has a date constraint
129 bool m_stale = false;
130 int m_caret = -1;
131 int m_visibleFirstRow = -1;
132 int m_visibleLastRow = -1;
133 int m_viewportFirst = -1;
134 int m_viewportLast = -1;
135 };
136