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% 4 / 0 / 4
Functions: 100.0% 4 / 0 / 4
Branches: 72.2% 13 / 0 / 18

app/TimeHistogram.h
Line Branch Exec Source
1 #pragma once
2
3 #include <QDate>
4 #include <QList>
5 #include <QLocale>
6 #include <QMap>
7 #include <QString>
8 #include <QVector>
9 #include <functional>
10
11 // Sprint 101 S101.1.4 (D-7, D-8): the pure calendar arithmetic behind the
12 // search timeline. No Qt Quick, no SQL, no clock: every function maps its
13 // arguments to a result, so the chart, the breadcrumb, the date chip and the
14 // drill-down share one definition of "the bucket" (rule 8 of SPRINT-101).
15 //
16 // Ranges are inclusive local calendar days. Counts arrive once per query as
17 // counts per local day (plus the 24 hours of a single-day range) and are
18 // re-bucketed here; zooming therefore never needs a new SQL aggregate.
19 namespace TimeHistogram {
20
21 enum class Unit { Year, Month, Week, Day, Hour };
22
23 struct Range {
24 QDate first;
25 QDate last;
26
4/6
✓ Branch 3 → 4 taken 67032 times.
✓ Branch 3 → 10 taken 3104 times.
✓ Branch 5 → 6 taken 67032 times.
✗ Branch 5 → 10 not taken.
✓ Branch 8 → 9 taken 67032 times.
✗ Branch 8 → 10 not taken.
70136 bool isValid() const { return first.isValid() && last.isValid() && first <= last; }
27
2/2
✓ Branch 3 → 4 taken 2215 times.
✓ Branch 3 → 6 taken 2 times.
2217 qint64 days() const { return isValid() ? first.daysTo(last) + 1 : 0; }
28
5/6
✓ Branch 3 → 4 taken 33021 times.
✗ Branch 3 → 11 not taken.
✓ Branch 6 → 7 taken 32332 times.
✓ Branch 6 → 11 taken 689 times.
✓ Branch 9 → 10 taken 5023 times.
✓ Branch 9 → 11 taken 27309 times.
33021 bool contains(const QDate &date) const { return isValid() && date >= first && date <= last; }
29
2/4
✓ Branch 3 → 4 taken 397 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 397 times.
✗ Branch 5 → 7 not taken.
397 bool operator==(const Range &other) const { return first == other.first && last == other.last; }
30 bool operator!=(const Range &other) const { return !(*this == other); }
31 };
32
33 struct Bucket {
34 Range range; // the days the bar covers, clipped to the shown range
35 int hour = -1; // 0..23 for Unit::Hour, else -1
36 qint64 count = 0;
37 };
38
39 // A breadcrumb level: "All time" (invalid range) or a calendar range.
40 struct Level {
41 Range range;
42 QString label;
43 };
44
45 // An axis label at a bucket boundary.
46 struct Tick {
47 int bucket = 0; // index of the bucket the label starts at
48 QString label;
49 };
50
51 // Readability cap: a timeline never shows more bars than this, whatever the
52 // width (P-45: all time → years, a year → months, a month → days, a day →
53 // hours). Narrow plots lower it further through maxBucketsForWidth().
54 constexpr int MaximumBuckets = 40;
55 // D-7: one bar needs at least this many pixels (bar + gap).
56 constexpr int MinimumBarPixels = 6;
57
58 int maxBucketsForWidth(qreal plotWidth);
59
60 // Number of buckets of `unit` the range spans (weeks start on weekStart).
61 int bucketCount(const Range &range, Unit unit, Qt::DayOfWeek weekStart);
62
63 // The finest unit whose bucket count fits maxBuckets. Hours only for a
64 // single-day range. Falls back to years.
65 Unit chooseUnit(const Range &range, int maxBuckets, Qt::DayOfWeek weekStart);
66
67 // The calendar unit containing `date`: its year, month, week or day.
68 Range unitRange(const QDate &date, Unit unit, Qt::DayOfWeek weekStart);
69
70 // The shown range for hits between first and last (no date constraint):
71 // extended to whole units of the chosen unit so bars are complete.
72 Range spanForHits(const QDate &firstHit, const QDate &lastHit, int maxBuckets, Qt::DayOfWeek weekStart);
73
74 // Day counts → one bucket per unit inside `range` (empty buckets included,
75 // clipped to the range). The sum of the bucket counts equals the sum of the
76 // day counts inside the range.
77 QList<Bucket> buckets(const QMap<QDate, qint64> &dayCounts, const Range &range, Unit unit,
78 Qt::DayOfWeek weekStart);
79 // The 24 hour buckets of one day.
80 QList<Bucket> hourBuckets(const QDate &day, const QVector<qint64> &hourCounts);
81
82 qint64 total(const QList<Bucket> &buckets);
83
84 // The range a drag from bucket a to bucket b covers (either order).
85 Range brush(const QList<Bucket> &buckets, int a, int b);
86
87 // D-8 zoom out: the enclosing calendar level of a range — a day or week →
88 // its month, a month → its year, a year or a range across years → all time
89 // (invalid range). A range inside one month goes to that month, inside one
90 // year to that year.
91 Range parentRange(const Range &range);
92
93 // The breadcrumb from "All time" down to `range` (the last level is range).
94 QList<Level> breadcrumb(const Range &range, const QLocale &locale, const QString &allTimeLabel);
95
96 // Human labels (never unit names): "2025", "March 2025", "10–16 March 2025",
97 // "Fri 14 March 2025", "14:00" — in the locale's formats.
98 QString rangeLabel(const Range &range, const QLocale &locale);
99 QString bucketLabel(const Bucket &bucket, Unit unit, const QLocale &locale);
100 // The short axis label of a bucket ("2025", "Mar", "10", "14").
101 QString axisLabel(const Bucket &bucket, Unit unit, const QLocale &locale);
102
103 // Collision-free axis labels: walks the buckets left to right and keeps a
104 // label only when it starts after the previous label ended (plus a gap).
105 // Month labels name the year on the first tick and on January.
106 // `textWidth` measures a label in pixels; `bucketWidth` is the slot width.
107 QList<Tick> ticks(const QList<Bucket> &buckets, Unit unit, const QLocale &locale, qreal bucketWidth,
108 const std::function<qreal(const QString &)> &textWidth, qreal gap = 8);
109
110 } // namespace TimeHistogram
111