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: 95.5% 128 / 0 / 134
Functions: 100.0% 11 / 0 / 11
Branches: 85.0% 204 / 0 / 240

data/PlaceGazetteer.cpp
Line Branch Exec Source
1 #include "PlaceGazetteer.h"
2
3 #include <algorithm>
4
5 #include <QRegularExpression>
6
7 namespace PlaceGazetteer {
8
9 namespace {
10
11 1253 Place placeAt(const GeoData &data, qint32 lat, qint32 lon, const QString &precision, int country) {
12 1253 Place place;
13
1/1
✓ Branch 4 → 5 taken 1253 times.
1253 place.position = GeoPoint::at(GeoData::degrees(lat), GeoData::degrees(lon));
14 1253 place.precision = precision;
15
3/6
✓ Branch 6 → 7 taken 1253 times.
✗ Branch 6 → 11 not taken.
✓ Branch 9 → 10 taken 1253 times.
✗ Branch 9 → 11 not taken.
✓ Branch 12 → 13 taken 1253 times.
✗ Branch 12 → 22 not taken.
1253 if (country >= 0 && country < data.countries().size()) {
16 1253 const GeoData::Country &c = data.countries().at(country);
17
1/1
✓ Branch 15 → 16 taken 1253 times.
1253 place.countryIso = QString::fromLatin1(c.iso2);
18
1/1
✓ Branch 18 → 19 taken 1253 times.
1253 place.timeZone = data.string(c.timeZone);
19 }
20 1253 return place;
21 ✗ }
22
23 1122 Place cityPlace(const GeoData &data, int index) {
24
1/1
✓ Branch 2 → 3 taken 1122 times.
1122 const GeoData::City city = data.city(index);
25
1/1
✓ Branch 6 → 7 taken 1122 times.
2244 Place place = placeAt(data, city.lat, city.lon, QStringLiteral("city"), city.country);
26
1/1
✓ Branch 9 → 10 taken 1122 times.
1122 place.placeName = data.string(city.name);
27
2/3
✓ Branch 12 → 13 taken 1122 times.
✓ Branch 14 → 15 taken 1122 times.
✗ Branch 14 → 16 not taken.
1122 if (const QString zone = data.timeZoneName(city.timeZone); !zone.isEmpty())
28 1122 place.timeZone = zone;
29 1122 return place;
30 ✗ }
31
32 22 Place countryPlace(const GeoData &data, int country) {
33 22 const GeoData::Country &c = data.countries().at(country);
34
1/1
✓ Branch 7 → 8 taken 22 times.
22 Place place = placeAt(data, c.labelLat, c.labelLon, QStringLiteral("country"), country);
35
1/1
✓ Branch 10 → 11 taken 22 times.
22 place.placeName = data.string(c.nameEn);
36 22 return place;
37 ✗ }
38
39 // The best city of that name: in the country when known, in the region when
40 // it matches, else the most populous one (cities are sorted by population).
41 1203 int bestCity(const GeoData &data, const QString &name, int country, int region) {
42
1/1
✓ Branch 2 → 3 taken 1203 times.
1203 const QString key = GeoData::key(name);
43
2/2
✓ Branch 4 → 5 taken 23 times.
✓ Branch 4 → 6 taken 1180 times.
1203 if (key.isEmpty())
44 23 return -1;
45
1/1
✓ Branch 6 → 7 taken 1180 times.
1180 QList<int> hits = data.citiesNamed(key, 64);
46 // A city called so beats one that has the name only as an alternate
47 // ("Porto" is an alternate name of Bordeaux); population decides within each.
48
3/3
✓ Branch 7 → 8 taken 1180 times.
✓ Branch 8 → 9 taken 1180 times.
✓ Branch 9 → 10 taken 1180 times.
1180 std::stable_partition(hits.begin(), hits.end(), [&data, &key](int index) {
49
3/3
✓ Branch 2 → 3 taken 5098 times.
✓ Branch 3 → 4 taken 5098 times.
✓ Branch 4 → 5 taken 5098 times.
5098 return GeoData::key(data.string(data.city(index).name)) == key;
50 });
51 1180 int inCountry = -1;
52
4/4
✓ Branch 10 → 11 taken 1180 times.
✓ Branch 11 → 12 taken 1180 times.
✓ Branch 26 → 13 taken 5073 times.
✓ Branch 26 → 27 taken 1178 times.
6251 for (int index : hits) {
53
1/1
✓ Branch 14 → 15 taken 5073 times.
5073 const GeoData::City city = data.city(index);
54
4/4
✓ Branch 15 → 16 taken 2357 times.
✓ Branch 15 → 18 taken 2716 times.
✓ Branch 16 → 17 taken 1790 times.
✓ Branch 16 → 18 taken 567 times.
5073 if (country >= 0 && city.country != country)
55 1790 continue;
56
4/4
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 21 taken 3280 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 19 → 21 taken 1 time.
3283 if (region >= 0 && city.admin1 == region)
57 2 return index;
58
2/2
✓ Branch 21 → 22 taken 1121 times.
✓ Branch 21 → 23 taken 2160 times.
3281 if (inCountry < 0)
59 1121 inCountry = index;
60 }
61
2/2
✓ Branch 27 → 28 taken 1120 times.
✓ Branch 27 → 29 taken 58 times.
1178 if (inCountry >= 0)
62 1120 return inCountry;
63
3/5
✓ Branch 29 → 30 taken 20 times.
✓ Branch 29 → 34 taken 38 times.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 34 taken 20 times.
✗ Branch 32 → 33 not taken.
58 return country < 0 && !hits.isEmpty() ? hits.first() : -1;
64 1203 }
65
66 // Forms of a locality to try: as written, without a leading postal code,
67 // before a district separator ("Berlin-Mitte", "Wien, 1. Bezirk",
68 // "Frankfurt am Main (Höchst)").
69 1090 QStringList localityForms(const QString &locality) {
70 1090 QStringList forms;
71
1/1
✓ Branch 2 → 3 taken 1090 times.
1090 QString text = locality.trimmed();
72
4/7
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 16 taken 1085 times.
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 16 not taken.
✓ Branch 10 → 11 taken 5 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
1095 static const QRegularExpression leadingCode(QStringLiteral("^[A-Za-z]{0,2}-?\\d{3,6}\\s+"));
73
1/1
✓ Branch 16 → 17 taken 1090 times.
1090 text.remove(leadingCode);
74
1/1
✓ Branch 17 → 18 taken 1090 times.
1090 forms << text;
75
2/2
✓ Branch 30 → 20 taken 4360 times.
✓ Branch 30 → 31 taken 1090 times.
5450 for (const QChar separator : {QLatin1Char('('), QLatin1Char(','), QLatin1Char('/'), QLatin1Char('-')}) {
76 4360 const int at = text.indexOf(separator);
77
2/2
✓ Branch 22 → 23 taken 8 times.
✓ Branch 22 → 29 taken 4352 times.
4360 if (at > 1)
78
3/3
✓ Branch 23 → 24 taken 8 times.
✓ Branch 24 → 25 taken 8 times.
✓ Branch 25 → 26 taken 8 times.
8 forms << text.left(at).trimmed();
79 }
80
1/1
✓ Branch 31 → 32 taken 1090 times.
1090 forms.removeDuplicates();
81 1090 return forms;
82 1090 }
83
84 1133 int countryOf(const GeoData &data, const QString &countryText, const QString &defaultCountry) {
85
3/3
✓ Branch 2 → 3 taken 1133 times.
✓ Branch 5 → 6 taken 456 times.
✓ Branch 5 → 8 taken 677 times.
1133 if (!countryText.trimmed().isEmpty())
86 456 return data.countryIndex(countryText);
87
2/2
✓ Branch 9 → 10 taken 561 times.
✓ Branch 9 → 11 taken 116 times.
677 return defaultCountry.isEmpty() ? -1 : data.countryIndex(defaultCountry);
88 }
89
90 } // namespace
91
92 1137 QString normalizedPostalCode(const QString &countryIso, const QString &input) {
93
2/2
✓ Branch 2 → 3 taken 1137 times.
✓ Branch 3 → 4 taken 1137 times.
1137 QString code = input.trimmed().toUpper();
94
4/7
✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 18 taken 1133 times.
✓ Branch 7 → 8 taken 4 times.
✗ Branch 7 → 18 not taken.
✓ Branch 12 → 13 taken 4 times.
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 94 not taken.
1141 static const QRegularExpression prefix(QStringLiteral("^(D|A|CH|DE|AT)[- ]"));
95
1/1
✓ Branch 18 → 19 taken 1137 times.
1137 code.remove(prefix);
96
1/1
✓ Branch 21 → 22 taken 1137 times.
1137 code.remove(QLatin1Char(' '));
97
4/7
✓ Branch 22 → 23 taken 4 times.
✓ Branch 22 → 35 taken 1133 times.
✓ Branch 24 → 25 taken 4 times.
✗ Branch 24 → 35 not taken.
✓ Branch 29 → 30 taken 4 times.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 106 not taken.
1141 static const QRegularExpression digits5(QStringLiteral("^\\d{5}$"));
98
4/7
✓ Branch 35 → 36 taken 4 times.
✓ Branch 35 → 48 taken 1133 times.
✓ Branch 37 → 38 taken 4 times.
✗ Branch 37 → 48 not taken.
✓ Branch 42 → 43 taken 4 times.
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 116 not taken.
1141 static const QRegularExpression digits4(QStringLiteral("^\\d{4}$"));
99
2/2
✓ Branch 50 → 51 taken 220 times.
✓ Branch 50 → 60 taken 917 times.
1137 if (countryIso == QLatin1String("DE"))
100
4/4
✓ Branch 52 → 53 taken 220 times.
✓ Branch 53 → 54 taken 220 times.
✓ Branch 54 → 55 taken 174 times.
✓ Branch 54 → 56 taken 46 times.
220 return digits5.match(code).hasMatch() ? code : QString();
101
6/6
✓ Branch 62 → 63 taken 902 times.
✓ Branch 62 → 66 taken 15 times.
✓ Branch 65 → 66 taken 17 times.
✓ Branch 65 → 67 taken 885 times.
✓ Branch 68 → 69 taken 32 times.
✓ Branch 68 → 78 taken 885 times.
917 if (countryIso == QLatin1String("AT") || countryIso == QLatin1String("CH"))
102
4/4
✓ Branch 70 → 71 taken 32 times.
✓ Branch 71 → 72 taken 32 times.
✓ Branch 72 → 73 taken 26 times.
✓ Branch 72 → 74 taken 6 times.
32 return digits4.match(code).hasMatch() ? code : QString();
103 885 return code;
104 1137 }
105
106 1019 QString addressKey(const ContactAddress &address) {
107 1019 QStringList parts;
108
2/2
✓ Branch 13 → 9 taken 5095 times.
✓ Branch 13 → 14 taken 1019 times.
11209 for (const QString &part : {address.street, address.postalCode, address.locality, address.region, address.country})
109
4/6
✓ Branch 9 → 10 taken 5095 times.
✓ Branch 10 → 11 taken 5095 times.
✓ Branch 15 → 16 taken 5095 times.
✓ Branch 15 → 19 taken 1019 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 35 not taken.
11209 parts << GeoData::key(part);
110
1/1
✓ Branch 22 → 23 taken 1019 times.
2038 return parts.join(QLatin1Char('|'));
111 1019 }
112
113 1133 Place locate(const GeoData &data, const ContactAddress &address, const QString &defaultCountry) {
114
1/1
✓ Branch 2 → 3 taken 1133 times.
1133 const int country = countryOf(data, address.country, defaultCountry);
115
3/3
✓ Branch 3 → 4 taken 564 times.
✓ Branch 3 → 7 taken 569 times.
✓ Branch 6 → 8 taken 564 times.
1133 const QString iso = country >= 0 ? QString::fromLatin1(data.countries().at(country).iso2) : QString();
116 // 1. Postal code (DE/AT/CH are bundled).
117
7/7
✓ Branch 8 → 9 taken 1133 times.
✓ Branch 10 → 11 taken 778 times.
✓ Branch 10 → 13 taken 355 times.
✓ Branch 11 → 12 taken 223 times.
✓ Branch 11 → 13 taken 555 times.
✓ Branch 14 → 15 taken 223 times.
✓ Branch 14 → 36 taken 910 times.
1133 if (const QString code = normalizedPostalCode(iso, address.postalCode); !code.isEmpty() && country >= 0) {
118
3/3
✓ Branch 15 → 16 taken 223 times.
✓ Branch 16 → 17 taken 106 times.
✓ Branch 16 → 36 taken 117 times.
223 if (const int postal = data.postalIndex(country, code); postal >= 0) {
119
1/1
✓ Branch 17 → 18 taken 106 times.
106 const GeoData::Postal entry = data.postal(postal);
120
1/3
✓ Branch 21 → 22 taken 106 times.
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
106 Place place = placeAt(data, entry.lat, entry.lon, QStringLiteral("postal"), country);
121
1/1
✓ Branch 24 → 25 taken 106 times.
106 place.placeName = data.string(entry.place);
122
1/1
✓ Branch 27 → 28 taken 106 times.
106 const int city = bestCity(data, place.placeName, country, -1);
123
2/2
✓ Branch 28 → 29 taken 92 times.
✓ Branch 28 → 33 taken 14 times.
106 if (city >= 0)
124
1/1
✓ Branch 29 → 30 taken 92 times.
92 place.timeZone = cityPlace(data, city).timeZone;
125
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 106 times.
106 return place;
126 ✗ }
127 1133 }
128 // 2. City (with the region as a tie-breaker).
129
2/2
✓ Branch 43 → 44 taken 5 times.
✓ Branch 43 → 47 taken 453 times.
458 const int region = country >= 0 && !address.region.isEmpty()
130
4/6
✓ Branch 40 → 42 taken 458 times.
✓ Branch 40 → 47 taken 569 times.
✓ Branch 44 → 45 taken 5 times.
✓ Branch 45 → 46 taken 5 times.
✗ Branch 118 → 119 not taken.
✗ Branch 118 → 120 not taken.
1490 ? data.admin1Index(country, GeoData::key(address.region))
131
2/2
✓ Branch 48 → 49 taken 5 times.
✓ Branch 48 → 50 taken 1022 times.
1027 : -1;
132
5/5
✓ Branch 50 → 51 taken 1027 times.
✓ Branch 51 → 52 taken 1027 times.
✓ Branch 52 → 53 taken 1027 times.
✓ Branch 61 → 54 taken 1032 times.
✓ Branch 61 → 62 taken 58 times.
1090 for (const QString &form : localityForms(address.locality))
133
3/3
✓ Branch 55 → 56 taken 1032 times.
✓ Branch 56 → 57 taken 969 times.
✓ Branch 56 → 59 taken 63 times.
1032 if (const int city = bestCity(data, form, country, region); city >= 0)
134
3/3
✓ Branch 57 → 58 taken 969 times.
✓ Branch 64 → 65 taken 58 times.
✓ Branch 64 → 67 taken 969 times.
1027 return cityPlace(data, city);
135 // 3. Region.
136
2/2
✓ Branch 66 → 68 taken 3 times.
✓ Branch 66 → 88 taken 55 times.
58 if (region >= 0) {
137 3 const GeoData::Admin1 &admin = data.admin1().at(region);
138
1/1
✓ Branch 73 → 74 taken 3 times.
3 Place place = placeAt(data, admin.lat, admin.lon, QStringLiteral("region"), country);
139
2/2
✓ Branch 76 → 77 taken 3 times.
✓ Branch 80 → 81 taken 3 times.
6 place.placeName = data.string(admin.names).section(QLatin1Char('|'), 0, 0);
140 3 return place;
141 3 }
142 // 4. Country — only when the address names one (a default country alone
143 // places nothing: an address of only a street is not "in Germany").
144
9/11
✓ Branch 88 → 89 taken 44 times.
✓ Branch 88 → 93 taken 11 times.
✓ Branch 89 → 90 taken 44 times.
✓ Branch 91 → 92 taken 20 times.
✓ Branch 91 → 93 taken 24 times.
✓ Branch 94 → 95 taken 44 times.
✓ Branch 94 → 96 taken 11 times.
✓ Branch 96 → 97 taken 20 times.
✓ Branch 96 → 99 taken 35 times.
✗ Branch 141 → 142 not taken.
✗ Branch 141 → 143 not taken.
55 if (country >= 0 && !address.country.trimmed().isEmpty())
145
1/1
✓ Branch 97 → 98 taken 20 times.
20 return countryPlace(data, country);
146 35 return {};
147 1133 }
148
149 65 Place locateText(const GeoData &data, const QString &text, const QString &defaultCountry) {
150 65 QStringList parts;
151
5/5
✓ Branch 5 → 6 taken 65 times.
✓ Branch 6 → 7 taken 65 times.
✓ Branch 7 → 8 taken 65 times.
✓ Branch 20 → 9 taken 78 times.
✓ Branch 20 → 21 taken 65 times.
143 for (const QString &part : text.split(QLatin1Char(','), Qt::SkipEmptyParts))
152
2/3
✓ Branch 10 → 11 taken 78 times.
✓ Branch 13 → 14 taken 78 times.
✗ Branch 13 → 18 not taken.
78 if (!part.trimmed().isEmpty())
153
2/2
✓ Branch 14 → 15 taken 78 times.
✓ Branch 15 → 16 taken 78 times.
143 parts << part.trimmed();
154
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 65 times.
65 if (parts.isEmpty())
155 ✗ return {};
156 65 int country = -1;
157
2/2
✓ Branch 26 → 27 taken 13 times.
✓ Branch 26 → 31 taken 52 times.
65 if (parts.size() > 1) {
158
2/2
✓ Branch 27 → 28 taken 13 times.
✓ Branch 28 → 29 taken 13 times.
13 country = data.countryIndex(parts.last());
159
2/2
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 12 times.
13 if (country >= 0)
160 1 parts.removeLast();
161 }
162
10/10
✓ Branch 31 → 32 taken 64 times.
✓ Branch 31 → 38 taken 1 time.
✓ Branch 33 → 34 taken 52 times.
✓ Branch 33 → 38 taken 12 times.
✓ Branch 34 → 35 taken 52 times.
✓ Branch 35 → 36 taken 52 times.
✓ Branch 36 → 37 taken 2 times.
✓ Branch 36 → 38 taken 50 times.
✓ Branch 39 → 40 taken 2 times.
✓ Branch 39 → 44 taken 63 times.
65 if (country < 0 && parts.size() == 1 && data.countryIndex(parts.first()) >= 0)
163
3/3
✓ Branch 40 → 41 taken 2 times.
✓ Branch 41 → 42 taken 2 times.
✓ Branch 42 → 43 taken 2 times.
2 return countryPlace(data, data.countryIndex(parts.first()));
164
2/2
✓ Branch 75 → 46 taken 63 times.
✓ Branch 75 → 76 taken 2 times.
65 for (int i = int(parts.size()) - 1; i >= 0; --i) {
165
5/5
✓ Branch 47 → 48 taken 63 times.
✓ Branch 48 → 49 taken 63 times.
✓ Branch 49 → 50 taken 63 times.
✓ Branch 68 → 51 taken 65 times.
✓ Branch 68 → 69 taken 2 times.
67 for (const QString &form : localityForms(parts.at(i))) {
166
1/1
✓ Branch 52 → 53 taken 65 times.
65 int city = bestCity(data, form, country, -1);
167
5/8
✓ Branch 53 → 54 taken 4 times.
✓ Branch 53 → 58 taken 61 times.
✓ Branch 54 → 55 taken 4 times.
✗ Branch 54 → 58 not taken.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 4 times.
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 63 taken 65 times.
65 if (city < 0 && country < 0 && !defaultCountry.isEmpty())
168 ✗ city = bestCity(data, form, data.countryIndex(defaultCountry), -1);
169
2/2
✓ Branch 63 → 64 taken 61 times.
✓ Branch 63 → 66 taken 4 times.
65 if (city >= 0)
170
1/1
✓ Branch 64 → 65 taken 61 times.
61 return cityPlace(data, city);
171 63 }
172 }
173
1/3
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 2 times.
✗ Branch 77 → 79 not taken.
2 return country >= 0 ? countryPlace(data, country) : Place{};
174 65 }
175
176 } // namespace PlaceGazetteer
177