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: 94.2% 178 / 0 / 189
Functions: 100.0% 33 / 0 / 33
Branches: 79.9% 294 / 0 / 368

app/TimeHistogram.cpp
Line Branch Exec Source
1 #include "TimeHistogram.h"
2
3 #include <QRegularExpression>
4 #include <QTime>
5 #include <algorithm>
6 #include <cmath>
7
8 namespace TimeHistogram {
9
10 namespace {
11
12 130946 QDate weekStartOf(const QDate &date, Qt::DayOfWeek weekStart) {
13 130946 return date.addDays(-((date.dayOfWeek() - int(weekStart) + 7) % 7));
14 }
15
16
3/3
✓ Branch 2 → 3 taken 88496 times.
✓ Branch 3 → 4 taken 88496 times.
✓ Branch 4 → 5 taken 88496 times.
88496 QDate monthStart(const QDate &date) { return QDate(date.year(), date.month(), 1); }
17
3/3
✓ Branch 2 → 3 taken 38755 times.
✓ Branch 3 → 4 taken 38755 times.
✓ Branch 4 → 5 taken 38755 times.
38755 QDate monthEnd(const QDate &date) { return monthStart(date).addMonths(1).addDays(-1); }
18
19 23237 bool wholeYear(const Range &range) {
20
8/8
✓ Branch 2 → 3 taken 23237 times.
✓ Branch 3 → 4 taken 23237 times.
✓ Branch 5 → 6 taken 2383 times.
✓ Branch 5 → 11 taken 20854 times.
✓ Branch 6 → 7 taken 2383 times.
✓ Branch 7 → 8 taken 2383 times.
✓ Branch 9 → 10 taken 672 times.
✓ Branch 9 → 11 taken 1711 times.
23237 return range.first == QDate(range.first.year(), 1, 1) && range.last == QDate(range.first.year(), 12, 31);
21 }
22 22967 bool wholeMonth(const Range &range) {
23
6/6
✓ Branch 2 → 3 taken 22967 times.
✓ Branch 4 → 5 taken 11981 times.
✓ Branch 4 → 9 taken 10986 times.
✓ Branch 5 → 6 taken 11981 times.
✓ Branch 7 → 8 taken 10970 times.
✓ Branch 7 → 9 taken 1011 times.
22967 return range.first == monthStart(range.first) && range.last == monthEnd(range.first);
24 }
25 1417 bool sameMonth(const Range &range) {
26
4/4
✓ Branch 4 → 5 taken 947 times.
✓ Branch 4 → 9 taken 470 times.
✓ Branch 7 → 8 taken 695 times.
✓ Branch 7 → 9 taken 252 times.
1417 return range.first.year() == range.last.year() && range.first.month() == range.last.month();
27 }
28
29 542531 Range clip(const Range &range, const Range &bounds) {
30 542531 return {std::max(range.first, bounds.first), std::min(range.last, bounds.last)};
31 }
32
33 // The locale's long date format without weekday and year ("d MMMM",
34 // "d. MMMM", "MMMM d"): day and month in the locale's order and words.
35 25294 QString dayMonthPattern(const QLocale &locale) {
36
1/1
✓ Branch 2 → 3 taken 25294 times.
25294 QString pattern = locale.dateFormat(QLocale::LongFormat);
37
2/2
✓ Branch 7 → 8 taken 25294 times.
✓ Branch 8 → 9 taken 25294 times.
25294 pattern.remove(QRegularExpression(QStringLiteral("d{3,4}[,.]?\\s*")));
38
2/2
✓ Branch 16 → 17 taken 25294 times.
✓ Branch 17 → 18 taken 25294 times.
25294 pattern.remove(QRegularExpression(QStringLiteral("[,\\s]*y+[.]?")));
39
2/2
✓ Branch 25 → 26 taken 25294 times.
✓ Branch 26 → 27 taken 25294 times.
25294 pattern.remove(QRegularExpression(QStringLiteral("\\s*'[^']*'\\s*$")));
40
1/1
✓ Branch 30 → 31 taken 25294 times.
25294 pattern = pattern.trimmed();
41
2/4
✓ Branch 35 → 36 taken 25294 times.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 25294 times.
✗ Branch 37 → 38 not taken.
25294 if (pattern.endsWith(QLatin1Char(','))) pattern.chop(1);
42
5/10
✓ Branch 40 → 41 taken 25294 times.
✓ Branch 41 → 42 taken 25294 times.
✗ Branch 41 → 47 not taken.
✓ Branch 44 → 45 taken 25294 times.
✓ Branch 45 → 46 taken 25294 times.
✗ Branch 45 → 47 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 53 taken 25294 times.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
75882 return pattern.contains(QLatin1Char('M')) && pattern.contains(QLatin1Char('d')) ? pattern : QStringLiteral("d MMMM");
43 25294 }
44
45 12126 bool monthFirst(const QLocale &locale) {
46
1/1
✓ Branch 2 → 3 taken 12126 times.
12126 const QString pattern = dayMonthPattern(locale);
47 24252 return pattern.indexOf(QLatin1Char('M')) < pattern.indexOf(QLatin1Char('d'));
48 12126 }
49
50 // The day-number part of the day-month pattern ("d" or "d.").
51 1574 QString dayPattern(const QLocale &locale) {
52
1/1
✓ Branch 2 → 3 taken 1574 times.
1574 const QString pattern = dayMonthPattern(locale);
53
7/7
✓ Branch 6 → 7 taken 1574 times.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 11 taken 1572 times.
✓ Branch 15 → 16 taken 1572 times.
✓ Branch 15 → 17 taken 2 times.
✓ Branch 17 → 18 taken 2 times.
✓ Branch 17 → 19 taken 1572 times.
4722 return pattern.contains(QStringLiteral("d.")) ? QStringLiteral("d.") : QStringLiteral("d");
54 1574 }
55
56 11997 QString withYear(const QString &text, int year, const QLocale &locale) {
57
10/14
✓ Branch 2 → 3 taken 11997 times.
✓ Branch 3 → 4 taken 11997 times.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 8 taken 11995 times.
✓ Branch 11 → 12 taken 11997 times.
✓ Branch 12 → 13 taken 11997 times.
✓ Branch 16 → 17 taken 11995 times.
✓ Branch 16 → 18 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 18 → 20 taken 11995 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
23994 return text + (monthFirst(locale) ? QStringLiteral(", ") : QStringLiteral(" ")) + QString::number(year);
58 }
59
60 10808 QString monthLabel(const QDate &date, const QLocale &locale, bool year) {
61
2/2
✓ Branch 2 → 3 taken 10808 times.
✓ Branch 3 → 4 taken 10808 times.
10808 const QString name = locale.standaloneMonthName(date.month(), QLocale::LongFormat);
62
10/14
✓ Branch 4 → 5 taken 10660 times.
✓ Branch 4 → 11 taken 148 times.
✓ Branch 5 → 6 taken 10660 times.
✓ Branch 6 → 7 taken 10660 times.
✓ Branch 9 → 10 taken 10660 times.
✓ Branch 10 → 12 taken 10660 times.
✓ Branch 13 → 14 taken 10660 times.
✓ Branch 13 → 15 taken 148 times.
✓ Branch 15 → 16 taken 10660 times.
✓ Branch 15 → 17 taken 148 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
32424 return year ? name + QLatin1Char(' ') + QString::number(date.year()) : name;
63 10808 }
64
65 // "10–16 March" (day-first locales) or "March 10–16"; across months
66 // "28 Feb – 6 Mar". The year is appended when asked for.
67 588 QString spanLabel(const Range &range, const QLocale &locale, bool year) {
68
1/1
✓ Branch 2 → 3 taken 588 times.
588 const QString pattern = dayMonthPattern(locale);
69 588 QString text;
70
3/3
✓ Branch 3 → 4 taken 588 times.
✓ Branch 4 → 5 taken 129 times.
✓ Branch 4 → 54 taken 459 times.
588 if (sameMonth(range)) {
71
1/1
✓ Branch 5 → 6 taken 129 times.
129 text = monthFirst(locale)
72
19/33
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 17 taken 127 times.
✓ Branch 10 → 11 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 16 → 25 taken 2 times.
✓ Branch 24 → 25 taken 127 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 41 → 43 taken 127 times.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 45 taken 127 times.
✓ Branch 45 → 46 taken 2 times.
✓ Branch 45 → 47 taken 127 times.
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 49 taken 127 times.
✓ Branch 49 → 50 taken 2 times.
✓ Branch 49 → 51 taken 127 times.
✓ Branch 51 → 52 taken 2 times.
✓ Branch 51 → 53 taken 127 times.
✗ Branch 144 → 145 not taken.
✗ Branch 144 → 146 not taken.
✗ Branch 148 → 149 not taken.
✗ Branch 148 → 150 not taken.
✗ Branch 152 → 153 not taken.
✗ Branch 152 → 154 not taken.
✗ Branch 156 → 157 not taken.
✗ Branch 156 → 158 not taken.
✗ Branch 160 → 161 not taken.
✗ Branch 160 → 162 not taken.
✗ Branch 164 → 165 not taken.
✗ Branch 164 → 166 not taken.
✗ Branch 168 → 169 not taken.
✗ Branch 168 → 170 not taken.
645 ? locale.toString(range.first, pattern) + QStringLiteral("–") + locale.toString(range.last, QStringLiteral("d"))
73
18/30
✓ Branch 17 → 18 taken 127 times.
✓ Branch 21 → 22 taken 127 times.
✓ Branch 22 → 23 taken 127 times.
✓ Branch 23 → 24 taken 127 times.
✓ Branch 27 → 28 taken 127 times.
✓ Branch 27 → 29 taken 2 times.
✓ Branch 29 → 30 taken 127 times.
✓ Branch 29 → 31 taken 2 times.
✓ Branch 31 → 32 taken 127 times.
✓ Branch 31 → 33 taken 2 times.
✓ Branch 33 → 34 taken 127 times.
✓ Branch 33 → 35 taken 2 times.
✓ Branch 35 → 36 taken 127 times.
✓ Branch 35 → 37 taken 2 times.
✓ Branch 37 → 38 taken 127 times.
✓ Branch 37 → 39 taken 2 times.
✓ Branch 39 → 40 taken 2 times.
✓ Branch 39 → 41 taken 127 times.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 126 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 130 not taken.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 138 not taken.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 142 not taken.
512 : locale.toString(range.first, dayPattern(locale)) + QStringLiteral("–") + locale.toString(range.last, pattern);
74 } else {
75
1/1
✓ Branch 61 → 62 taken 459 times.
1377 const QString shortPattern = QString(pattern).replace(QStringLiteral("MMMM"), QStringLiteral("MMM"));
76
4/4
✓ Branch 68 → 69 taken 459 times.
✓ Branch 72 → 73 taken 459 times.
✓ Branch 73 → 74 taken 459 times.
✓ Branch 74 → 75 taken 459 times.
918 text = locale.toString(range.first, shortPattern) + QStringLiteral(" – ") + locale.toString(range.last, shortPattern);
77
4/4
✓ Branch 82 → 83 taken 459 times.
✓ Branch 83 → 84 taken 459 times.
✓ Branch 84 → 85 taken 442 times.
✓ Branch 84 → 105 taken 17 times.
459 if (range.first.year() != range.last.year())
78
4/4
✓ Branch 91 → 92 taken 442 times.
✓ Branch 92 → 93 taken 442 times.
✓ Branch 93 → 94 taken 442 times.
✓ Branch 94 → 95 taken 442 times.
884 return withYear(locale.toString(range.first, shortPattern), range.first.year(), locale) + QStringLiteral(" – ") +
79
4/4
✓ Branch 85 → 86 taken 442 times.
✓ Branch 86 → 87 taken 442 times.
✓ Branch 87 → 88 taken 442 times.
✓ Branch 95 → 96 taken 442 times.
1326 withYear(locale.toString(range.last, shortPattern), range.last.year(), locale);
80 459 }
81
4/4
✓ Branch 111 → 112 taken 108 times.
✓ Branch 111 → 114 taken 38 times.
✓ Branch 112 → 113 taken 108 times.
✓ Branch 113 → 115 taken 108 times.
146 return year ? withYear(text, range.last.year(), locale) : text;
82 588 }
83
84 11006 QString dayLabel(const QDate &date, const QLocale &locale, bool year) {
85
4/4
✓ Branch 2 → 3 taken 11006 times.
✓ Branch 3 → 4 taken 11006 times.
✓ Branch 7 → 8 taken 11006 times.
✓ Branch 8 → 9 taken 11006 times.
22012 const QString text = locale.toString(date, QStringLiteral("ddd ")) + locale.toString(date, dayMonthPattern(locale));
86
4/4
✓ Branch 14 → 15 taken 11005 times.
✓ Branch 14 → 17 taken 1 time.
✓ Branch 15 → 16 taken 11005 times.
✓ Branch 16 → 18 taken 11005 times.
22012 return year ? withYear(text, date.year(), locale) : text;
87 11006 }
88
89 // Shortest label of a breadcrumb level below `parent` (the year is implied
90 // by a year level above it).
91 446 QString levelLabel(const Range &range, const Range &parent, const QLocale &locale) {
92
4/6
✓ Branch 3 → 4 taken 187 times.
✓ Branch 3 → 11 taken 259 times.
✓ Branch 6 → 7 taken 187 times.
✗ Branch 6 → 11 not taken.
✓ Branch 9 → 10 taken 187 times.
✗ Branch 9 → 11 not taken.
446 const bool yearShown = parent.isValid() && parent.first.year() == range.first.year() && parent.last.year() == range.last.year();
93
2/2
✓ Branch 13 → 14 taken 232 times.
✓ Branch 13 → 16 taken 214 times.
446 if (wholeYear(range)) return QString::number(range.first.year());
94
2/2
✓ Branch 17 → 18 taken 148 times.
✓ Branch 17 → 19 taken 66 times.
214 if (wholeMonth(range)) return monthLabel(range.first, locale, !yearShown);
95
2/2
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 65 times.
66 if (range.first == range.last) return dayLabel(range.first, locale, !yearShown);
96 65 return spanLabel(range, locale, !yearShown);
97 }
98
99 602 QList<int> tickSteps(Unit unit) {
100
4/6
✓ Branch 2 → 3 taken 23 times.
✓ Branch 2 → 4 taken 416 times.
✓ Branch 2 → 5 taken 4 times.
✓ Branch 2 → 6 taken 159 times.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
602 switch (unit) {
101 23 case Unit::Year: return {1, 2, 5, 10, 20, 50};
102 416 case Unit::Month: return {1, 2, 3, 6, 12};
103 4 case Unit::Week: return {1, 2, 4, 8, 13, 26};
104 159 case Unit::Day: return {1, 2, 7, 14, 28};
105 ✗ case Unit::Hour: return {1, 2, 3, 6, 12};
106 }
107 ✗ return {1};
108 }
109
110 } // namespace
111
112 1128 int maxBucketsForWidth(qreal plotWidth) {
113
1/1
✓ Branch 2 → 3 taken 1128 times.
1128 return std::clamp(int(std::floor(plotWidth / MinimumBarPixels)), 1, MaximumBuckets);
114 }
115
116 2889 int bucketCount(const Range &range, Unit unit, Qt::DayOfWeek weekStart) {
117
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2889 times.
2889 if (!range.isValid()) return 0;
118
4/6
✓ Branch 5 → 6 taken 40 times.
✓ Branch 5 → 9 taken 898 times.
✓ Branch 5 → 14 taken 913 times.
✓ Branch 5 → 19 taken 1038 times.
✗ Branch 5 → 21 not taken.
✗ Branch 5 → 23 not taken.
2889 switch (unit) {
119 40 case Unit::Year: return range.last.year() - range.first.year() + 1;
120 898 case Unit::Month:
121 898 return (range.last.year() * 12 + range.last.month()) - (range.first.year() * 12 + range.first.month()) + 1;
122 913 case Unit::Week:
123
3/3
✓ Branch 14 → 15 taken 913 times.
✓ Branch 15 → 16 taken 913 times.
✓ Branch 16 → 17 taken 913 times.
913 return int(weekStartOf(range.first, weekStart).daysTo(weekStartOf(range.last, weekStart)) / 7) + 1;
124 1038 case Unit::Day: return int(range.days());
125 ✗ case Unit::Hour: return int(range.days()) * 24;
126 }
127 ✗ return 0;
128 }
129
130 1138 Unit chooseUnit(const Range &range, int maxBuckets, Qt::DayOfWeek weekStart) {
131
5/6
✓ Branch 3 → 4 taken 1138 times.
✗ Branch 3 → 7 not taken.
✓ Branch 5 → 6 taken 140 times.
✓ Branch 5 → 7 taken 998 times.
✓ Branch 8 → 9 taken 140 times.
✓ Branch 8 → 10 taken 998 times.
1138 if (range.isValid() && range.days() == 1) return Unit::Hour;
132
2/2
✓ Branch 16 → 12 taken 2728 times.
✓ Branch 16 → 17 taken 66 times.
2794 for (const Unit unit : {Unit::Day, Unit::Week, Unit::Month})
133
3/3
✓ Branch 12 → 13 taken 2728 times.
✓ Branch 13 → 14 taken 932 times.
✓ Branch 13 → 15 taken 1796 times.
2728 if (bucketCount(range, unit, weekStart) <= maxBuckets) return unit;
134 66 return Unit::Year;
135 }
136
137 555694 Range unitRange(const QDate &date, Unit unit, Qt::DayOfWeek weekStart) {
138
4/5
✓ Branch 2 → 3 taken 1502 times.
✓ Branch 2 → 7 taken 26370 times.
✓ Branch 2 → 9 taken 64560 times.
✓ Branch 2 → 14 taken 463262 times.
✗ Branch 2 → 15 not taken.
555694 switch (unit) {
139 1502 case Unit::Year: return {QDate(date.year(), 1, 1), QDate(date.year(), 12, 31)};
140 26370 case Unit::Month: return {monthStart(date), monthEnd(date)};
141
2/2
✓ Branch 10 → 11 taken 64560 times.
✓ Branch 11 → 12 taken 64560 times.
64560 case Unit::Week: return {weekStartOf(date, weekStart), weekStartOf(date, weekStart).addDays(6)};
142 463262 case Unit::Day:
143 463262 case Unit::Hour: return {date, date};
144 }
145 ✗ return {date, date};
146 }
147
148 552 Range spanForHits(const QDate &firstHit, const QDate &lastHit, int maxBuckets, Qt::DayOfWeek weekStart) {
149 552 const Range hits{std::min(firstHit, lastHit), std::max(firstHit, lastHit)};
150
3/3
✓ Branch 4 → 5 taken 552 times.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 5 → 7 taken 551 times.
552 if (!hits.isValid()) return {};
151
1/1
✓ Branch 7 → 8 taken 551 times.
551 const Unit unit = chooseUnit(hits, maxBuckets, weekStart);
152
4/4
✓ Branch 8 → 9 taken 471 times.
✓ Branch 8 → 10 taken 80 times.
✓ Branch 9 → 10 taken 25 times.
✓ Branch 9 → 11 taken 446 times.
551 if (unit == Unit::Hour || unit == Unit::Day) return hits;
153
2/2
✓ Branch 11 → 12 taken 446 times.
✓ Branch 12 → 13 taken 446 times.
446 return {unitRange(hits.first, unit, weekStart).first, unitRange(hits.last, unit, weekStart).last};
154 }
155
156 1256 QList<Bucket> buckets(const QMap<QDate, qint64> &dayCounts, const Range &range, Unit unit,
157 Qt::DayOfWeek weekStart) {
158 1256 QList<Bucket> result;
159
4/7
✓ Branch 2 → 3 taken 1256 times.
✓ Branch 3 → 4 taken 1256 times.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1256 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1256 times.
1256 if (!range.isValid() || unit == Unit::Hour) return result;
160 1256 QDate cursor = range.first;
161
2/2
✓ Branch 31 → 10 taken 542531 times.
✓ Branch 31 → 32 taken 1256 times.
543787 while (cursor <= range.last) {
162 542531 Bucket bucket;
163
2/2
✓ Branch 10 → 11 taken 542531 times.
✓ Branch 11 → 12 taken 542531 times.
542531 bucket.range = clip(unitRange(cursor, unit, weekStart), range);
164
8/8
✓ Branch 12 → 13 taken 542531 times.
✓ Branch 16 → 17 taken 792124 times.
✓ Branch 18 → 19 taken 785236 times.
✓ Branch 18 → 24 taken 6888 times.
✓ Branch 22 → 23 taken 249593 times.
✓ Branch 22 → 24 taken 535643 times.
✓ Branch 25 → 14 taken 249593 times.
✓ Branch 25 → 26 taken 542531 times.
792124 for (auto it = dayCounts.lowerBound(bucket.range.first); it != dayCounts.cend() && it.key() <= bucket.range.last; ++it)
165 249593 bucket.count += it.value();
166
1/1
✓ Branch 26 → 27 taken 542531 times.
542531 result.append(bucket);
167
1/1
✓ Branch 27 → 28 taken 542531 times.
542531 cursor = bucket.range.last.addDays(1);
168 }
169 1256 return result;
170 ✗ }
171
172 2 QList<Bucket> hourBuckets(const QDate &day, const QVector<qint64> &hourCounts) {
173 2 QList<Bucket> result;
174
2/2
✓ Branch 10 → 3 taken 48 times.
✓ Branch 10 → 11 taken 2 times.
50 for (int hour = 0; hour < 24; ++hour)
175
2/3
✓ Branch 4 → 5 taken 48 times.
✗ Branch 4 → 7 not taken.
✓ Branch 8 → 9 taken 48 times.
48 result.append({{day, day}, hour, hour < hourCounts.size() ? hourCounts.at(hour) : 0});
176 2 return result;
177 ✗ }
178
179 657 qint64 total(const QList<Bucket> &buckets) {
180 657 qint64 sum = 0;
181
2/2
✓ Branch 7 → 4 taken 525473 times.
✓ Branch 7 → 8 taken 657 times.
526130 for (const Bucket &bucket : buckets) sum += bucket.count;
182 657 return sum;
183 }
184
185 6 Range brush(const QList<Bucket> &buckets, int a, int b) {
186
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 5 times.
6 if (buckets.isEmpty()) return {};
187
1/1
✓ Branch 7 → 8 taken 5 times.
5 const int first = std::clamp(std::min(a, b), 0, int(buckets.size()) - 1);
188
1/1
✓ Branch 10 → 11 taken 5 times.
5 const int last = std::clamp(std::max(a, b), 0, int(buckets.size()) - 1);
189 5 return {buckets.at(first).range.first, buckets.at(last).range.last};
190 }
191
192 829 Range parentRange(const Range &range) {
193
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 829 times.
829 if (!range.isValid()) return {};
194
6/6
✓ Branch 6 → 7 taken 566 times.
✓ Branch 6 → 10 taken 263 times.
✓ Branch 8 → 9 taken 404 times.
✓ Branch 8 → 10 taken 162 times.
✓ Branch 11 → 12 taken 404 times.
✓ Branch 11 → 14 taken 425 times.
829 if (sameMonth(range) && !wholeMonth(range)) return {monthStart(range.first), monthEnd(range.first)};
195
6/6
✓ Branch 16 → 17 taken 397 times.
✓ Branch 16 → 20 taken 28 times.
✓ Branch 18 → 19 taken 164 times.
✓ Branch 18 → 20 taken 233 times.
✓ Branch 21 → 22 taken 164 times.
✓ Branch 21 → 26 taken 261 times.
425 if (range.first.year() == range.last.year() && !wholeYear(range))
196 164 return {QDate(range.first.year(), 1, 1), QDate(range.first.year(), 12, 31)};
197 261 return {};
198 }
199
200 2269 QList<Level> breadcrumb(const Range &range, const QLocale &locale, const QString &allTimeLabel) {
201 2269 QList<Range> chain;
202
5/5
✓ Branch 3 → 4 taken 446 times.
✓ Branch 4 → 5 taken 446 times.
✓ Branch 5 → 6 taken 2715 times.
✓ Branch 6 → 3 taken 446 times.
✓ Branch 6 → 7 taken 2269 times.
2715 for (Range level = range; level.isValid(); level = parentRange(level)) chain.prepend(level);
203
2/4
✓ Branch 10 → 11 taken 2269 times.
✓ Branch 10 → 12 taken 2269 times.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
4538 QList<Level> levels{{Range{}, allTimeLabel}};
204 2269 Range parent;
205
4/4
✓ Branch 12 → 13 taken 2269 times.
✓ Branch 13 → 14 taken 2269 times.
✓ Branch 21 → 15 taken 446 times.
✓ Branch 21 → 22 taken 2269 times.
2715 for (const Range &level : chain) {
206
1/1
✓ Branch 17 → 18 taken 446 times.
446 levels.append({level, levelLabel(level, parent, locale)});
207 446 parent = level;
208 }
209 2269 return levels;
210 4984 }
211
212 22394 QString rangeLabel(const Range &range, const QLocale &locale) {
213
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 22394 times.
22394 if (!range.isValid()) return {};
214
2/2
✓ Branch 6 → 7 taken 207 times.
✓ Branch 6 → 9 taken 22187 times.
22394 if (wholeYear(range)) return QString::number(range.first.year());
215
2/2
✓ Branch 10 → 11 taken 10660 times.
✓ Branch 10 → 12 taken 11527 times.
22187 if (wholeMonth(range)) return monthLabel(range.first, locale, true);
216
2/2
✓ Branch 13 → 14 taken 11004 times.
✓ Branch 13 → 15 taken 523 times.
11527 if (range.first == range.last) return dayLabel(range.first, locale, true);
217 523 return spanLabel(range, locale, true);
218 }
219
220 9353 QString bucketLabel(const Bucket &bucket, Unit unit, const QLocale &locale) {
221
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 18 taken 9352 times.
9353 if (unit == Unit::Hour)
222
2/2
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
2 return dayLabel(bucket.range.first, locale, true) + QStringLiteral(", ") +
223
3/3
✓ Branch 3 → 4 taken 1 time.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 10 → 11 taken 1 time.
3 locale.toString(QTime(bucket.hour, 0), QLocale::ShortFormat);
224 9352 return rangeLabel(bucket.range, locale);
225 }
226
227 6815 QString axisLabel(const Bucket &bucket, Unit unit, const QLocale &locale) {
228
3/5
✓ Branch 2 → 3 taken 163 times.
✓ Branch 2 → 5 taken 5205 times.
✓ Branch 2 → 7 taken 1447 times.
✗ Branch 2 → 12 not taken.
✗ Branch 2 → 15 not taken.
6815 switch (unit) {
229 163 case Unit::Year: return QString::number(bucket.range.first.year());
230 5205 case Unit::Month: return locale.standaloneMonthName(bucket.range.first.month(), QLocale::ShortFormat);
231 1447 case Unit::Week:
232
2/2
✓ Branch 7 → 8 taken 1447 times.
✓ Branch 8 → 9 taken 1447 times.
1447 case Unit::Day: return locale.toString(bucket.range.first, dayPattern(locale));
233 ✗ case Unit::Hour: return locale.toString(QTime(bucket.hour, 0), QLocale::ShortFormat);
234 }
235 ✗ return {};
236 }
237
238 namespace {
239 // Month ticks carry the year where it starts and on the first tick ("Jan
240 // 2021", "Apr 2020"), so two "Jan" are never ambiguous.
241 6814 QString tickLabel(const Bucket &bucket, Unit unit, const QLocale &locale, bool first) {
242
1/1
✓ Branch 2 → 3 taken 6814 times.
6814 const QString label = axisLabel(bucket, unit, locale);
243
9/9
✓ Branch 3 → 4 taken 5204 times.
✓ Branch 3 → 8 taken 1610 times.
✓ Branch 4 → 5 taken 4273 times.
✓ Branch 4 → 7 taken 931 times.
✓ Branch 5 → 6 taken 4273 times.
✓ Branch 6 → 7 taken 148 times.
✓ Branch 6 → 8 taken 4125 times.
✓ Branch 9 → 10 taken 1079 times.
✓ Branch 9 → 20 taken 5735 times.
6814 if (unit == Unit::Month && (first || bucket.range.first.month() == 1))
244
4/4
✓ Branch 10 → 11 taken 1079 times.
✓ Branch 11 → 12 taken 1079 times.
✓ Branch 14 → 15 taken 1079 times.
✓ Branch 15 → 16 taken 1079 times.
1079 return label + QLatin1Char(' ') + QString::number(bucket.range.first.year());
245 5735 return label;
246 6814 }
247 } // namespace
248
249 602 QList<Tick> ticks(const QList<Bucket> &buckets, Unit unit, const QLocale &locale, qreal bucketWidth,
250 const std::function<qreal(const QString &)> &textWidth, qreal gap) {
251 1164 const auto labelled = [&](int step) {
252 1164 QList<Tick> result;
253 1164 qreal previousRight = -1e9;
254
2/2
✓ Branch 19 → 3 taken 6814 times.
✓ Branch 19 → 20 taken 602 times.
7416 for (int i = 0; i < buckets.size(); i += step) {
255
1/1
✓ Branch 4 → 5 taken 6814 times.
6814 const QString label = tickLabel(buckets.at(i), unit, locale, i == 0);
256
1/1
✓ Branch 5 → 6 taken 6814 times.
6814 const qreal width = textWidth(label);
257 6814 const qreal left = (i + 0.5) * bucketWidth - width / 2;
258
2/2
✓ Branch 6 → 7 taken 562 times.
✓ Branch 6 → 9 taken 6252 times.
6814 if (left < previousRight + gap) return QList<Tick>{};
259
1/1
✓ Branch 10 → 11 taken 6252 times.
6252 result.append({i, label});
260 6252 previousRight = left + width;
261 6814 }
262 602 return result;
263 7416 };
264
4/5
✓ Branch 2 → 3 taken 602 times.
✓ Branch 3 → 4 taken 602 times.
✓ Branch 4 → 5 taken 602 times.
✓ Branch 23 → 6 taken 1164 times.
✗ Branch 23 → 24 not taken.
1164 for (const int step : tickSteps(unit)) {
265
1/1
✓ Branch 7 → 8 taken 1164 times.
1164 const auto result = labelled(step);
266
6/6
✓ Branch 9 → 10 taken 563 times.
✓ Branch 9 → 12 taken 601 times.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 562 times.
✓ Branch 14 → 15 taken 602 times.
✓ Branch 14 → 16 taken 562 times.
1164 if (!result.isEmpty() || buckets.isEmpty()) return result;
267 1164 }
268 ✗ return buckets.isEmpty() ? QList<Tick>{} : QList<Tick>{{0, tickLabel(buckets.first(), unit, locale, true)}};
269 ✗ }
270
271 } // namespace TimeHistogram
272