File indexing completed on 2024-05-12 05:17:31
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <KItinerary/CountryDb> 0008 0009 #include <config-kitinerary.h> 0010 #include "knowledgedb/alphaid.h" 0011 #include <knowledgedb/timezonedb.cpp> 0012 #include "knowledgedb/trainstationdb.h" 0013 0014 #include <QDebug> 0015 #include <QObject> 0016 #include <QTest> 0017 #include <QTimeZone> 0018 0019 using namespace KItinerary; 0020 using namespace KItinerary::KnowledgeDb; 0021 0022 namespace KItinerary { namespace KnowledgeDb { 0023 char *toString(CountryId c) 0024 { 0025 using QTest::toString; 0026 return toString(c.toString()); 0027 } 0028 }} 0029 0030 char *toString(const QTimeZone &tz) 0031 { 0032 using QTest::toString; 0033 return toString(tz.id()); 0034 } 0035 0036 class KnowledgeDbTest : public QObject 0037 { 0038 Q_OBJECT 0039 private Q_SLOTS: 0040 void testUnalignedNumber() 0041 { 0042 constexpr UnalignedNumber<3> uic1(8001337); 0043 constexpr UnalignedNumber<3> uic2(8001330); 0044 static_assert(sizeof(uic1) == 3, ""); 0045 static_assert(alignof(UnalignedNumber<3>) == 1, ""); 0046 QVERIFY(!(uic1 < uic2)); 0047 QVERIFY(uic2 < uic1); 0048 QVERIFY(uic1 != uic2); 0049 QVERIFY(!(uic1 == uic2)); 0050 0051 constexpr UnalignedNumber<3> uic3(9899776); 0052 constexpr UnalignedNumber<3> uic4(1000191); 0053 QVERIFY(!(uic3 < uic4)); 0054 QVERIFY(uic4 < uic3); 0055 QVERIFY(uic3 != uic4); 0056 QVERIFY(!(uic3 == uic4)); 0057 QCOMPARE(uic3.value(), 9899776); 0058 QCOMPARE(uic4.value(), 1000191); 0059 0060 constexpr UnalignedNumber<3> uic[2] = { UnalignedNumber<3>(8301700), UnalignedNumber<3>(8301701) }; 0061 static_assert(sizeof(uic) == 6, ""); 0062 QVERIFY(uic[0] < uic[1]); 0063 QVERIFY(uic[0] == uic[0]); 0064 QVERIFY(uic[0] != uic[1]); 0065 } 0066 0067 void testAlphaId() 0068 { 0069 using ID3 = AlphaId<uint16_t, 3>; 0070 constexpr ID3 id1{"ABC"}; 0071 const ID3 id2(QStringLiteral("CBA")); 0072 static_assert(sizeof(id1) == 2, ""); 0073 QVERIFY(id1.isValid()); 0074 QVERIFY(id2.isValid()); 0075 QVERIFY(id1 < id2); 0076 QVERIFY(!(id2 < id1)); 0077 QVERIFY(id1 == id1); 0078 QVERIFY(id1 != id2); 0079 QVERIFY(!(id1 == id2)); 0080 QVERIFY(!(id1 != id1)); 0081 0082 QCOMPARE(id1.toString(), QLatin1StringView("ABC")); 0083 QCOMPARE(id2.toString(), QLatin1StringView("CBA")); 0084 0085 constexpr ID3 id3; 0086 QVERIFY(!id3.isValid()); 0087 QVERIFY(id3.toString().isEmpty()); 0088 0089 qDebug() << id1; 0090 } 0091 0092 void testStationIdentifiers() 0093 { 0094 auto sncf = KnowledgeDb::SncfStationId(QStringLiteral("FRPNO")); 0095 QVERIFY(sncf.isValid()); 0096 QCOMPARE(sncf.toString(), QLatin1StringView("FRPNO")); 0097 sncf = KnowledgeDb::SncfStationId(QStringLiteral("Abc")); 0098 QVERIFY(!sncf.isValid()); 0099 sncf = KnowledgeDb::SncfStationId(QStringLiteral("CHZID")); 0100 QVERIFY(sncf.isValid()); 0101 QCOMPARE(sncf.toString(), QLatin1StringView("CHZID")); 0102 0103 auto vrCode = KnowledgeDb::VRStationCode(QStringLiteral("HSL")); 0104 QVERIFY(vrCode.isValid()); 0105 QCOMPARE(vrCode.toString(), QLatin1StringView("HSL")); 0106 } 0107 0108 void testIBNRLookup() 0109 { 0110 auto station = KnowledgeDb::stationForIbnr(IBNR{1234567}); 0111 QVERIFY(!station.coordinate.isValid()); 0112 0113 station = KnowledgeDb::stationForIbnr({}); 0114 QVERIFY(!station.coordinate.isValid()); 0115 0116 station = KnowledgeDb::stationForIbnr(IBNR{8011160}); 0117 QVERIFY(station.coordinate.isValid()); 0118 QCOMPARE(station.country, CountryId{"DE"}); 0119 0120 station = KnowledgeDb::stationForIbnr(IBNR{8501687}); 0121 QVERIFY(station.coordinate.isValid()); 0122 QCOMPARE(station.country, CountryId{"CH"}); 0123 0124 // Aachen West, very close to the NL border, should be in DE timezone 0125 station = KnowledgeDb::stationForIbnr(IBNR{8000404}); 0126 QVERIFY(station.coordinate.isValid()); 0127 QCOMPARE(station.country, CountryId{"DE"}); 0128 0129 // Berlin Gesundbrunnen has complex closing/reopening times in Wikidata that can confuse the generator 0130 station = KnowledgeDb::stationForIbnr(IBNR{8011102}); 0131 QVERIFY(station.coordinate.isValid()); 0132 QCOMPARE(station.country, CountryId{"DE"}); 0133 } 0134 0135 void testUICLookup() 0136 { 0137 auto station = KnowledgeDb::stationForUic(UICStation{1234567}); 0138 QVERIFY(!station.coordinate.isValid()); 0139 0140 station = KnowledgeDb::stationForUic({}); 0141 QVERIFY(!station.coordinate.isValid()); 0142 0143 station = KnowledgeDb::stationForUic(UICStation{1001332}); 0144 QVERIFY(station.coordinate.isValid()); 0145 QCOMPARE(station.country, CountryId{"FI"}); 0146 0147 // unassigned UIC code, but ambigiously used in DB tickets and SNCF API 0148 station = KnowledgeDb::stationForUic(UICStation{8003137}); 0149 QVERIFY(!station.coordinate.isValid()); 0150 } 0151 0152 void testSncfStationIdLookup() 0153 { 0154 auto station = KnowledgeDb::stationForSncfStationId({}); 0155 QVERIFY(!station.coordinate.isValid()); 0156 0157 station = KnowledgeDb::stationForSncfStationId(SncfStationId{"XXXXX"}); 0158 QVERIFY(!station.coordinate.isValid()); 0159 0160 station = KnowledgeDb::stationForSncfStationId(SncfStationId{"FRAES"}); 0161 QVERIFY(station.coordinate.isValid()); 0162 QCOMPARE(station.country, CountryId{"FR"}); 0163 0164 station = KnowledgeDb::stationForSncfStationId(SncfStationId{QStringLiteral("FRXYT")}); 0165 QVERIFY(station.coordinate.isValid()); 0166 QCOMPARE(station.country, CountryId{"FR"}); 0167 0168 station = KnowledgeDb::stationForSncfStationId(SncfStationId{"CHGVA"}); 0169 QVERIFY(station.coordinate.isValid()); 0170 QCOMPARE(station.country, CountryId{"CH"}); 0171 station = KnowledgeDb::stationForSncfStationId(SncfStationId{"FRHWO"}); // alias for CHGVA... 0172 QVERIFY(station.coordinate.isValid()); 0173 QCOMPARE(station.country, CountryId{"CH"}); 0174 0175 station = KnowledgeDb::stationForSncfStationId(SncfStationId{"NLAMA"}); // vs. SNCB ID of NLASC 0176 QVERIFY(station.coordinate.isValid()); 0177 QCOMPARE(station.country, CountryId{"NL"}); 0178 } 0179 0180 void testBenerailStationIdLookup() 0181 { 0182 auto station = KnowledgeDb::stationForBenerailId({}); 0183 QVERIFY(!station.coordinate.isValid()); 0184 0185 station = KnowledgeDb::stationForBenerailId(BenerailStationId{"XXXXX"}); 0186 QVERIFY(!station.coordinate.isValid()); 0187 0188 station = KnowledgeDb::stationForBenerailId(BenerailStationId{"NLASC"}); 0189 QVERIFY(station.coordinate.isValid()); 0190 QCOMPARE(station.country, CountryId{"NL"}); 0191 } 0192 0193 void testCountryDb() 0194 { 0195 auto country = KnowledgeDb::countryForId(CountryId{}); 0196 QCOMPARE(country.drivingSide, KnowledgeDb::DrivingSide::Unknown); 0197 QCOMPARE(country.powerPlugTypes, {Unknown}); 0198 0199 country = KnowledgeDb::countryForId(CountryId{"DE"}); 0200 QCOMPARE(country.drivingSide, KnowledgeDb::DrivingSide::Right); 0201 QCOMPARE(country.powerPlugTypes, KnowledgeDb::PowerPlugTypes{TypeC|TypeF}); 0202 country = KnowledgeDb::countryForId(CountryId{"GB"}); 0203 QCOMPARE(country.drivingSide, KnowledgeDb::DrivingSide::Left); 0204 QCOMPARE(country.powerPlugTypes, {TypeG}); 0205 country = KnowledgeDb::countryForId(CountryId{"GL"}); 0206 QCOMPARE(country.drivingSide, KnowledgeDb::DrivingSide::Right); 0207 } 0208 0209 void testPowerPlugCompat_data() 0210 { 0211 using namespace KnowledgeDb; 0212 0213 QTest::addColumn<PowerPlugTypes>("plugs"); 0214 QTest::addColumn<PowerPlugTypes>("sockets"); 0215 QTest::addColumn<PowerPlugTypes>("failPlugs"); 0216 QTest::addColumn<PowerPlugTypes>("failSockets"); 0217 0218 QTest::newRow("empty") << PowerPlugTypes{} << PowerPlugTypes{} << PowerPlugTypes{} << PowerPlugTypes{}; 0219 QTest::newRow("DE-DE") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{} << PowerPlugTypes{}; 0220 QTest::newRow("DE-CH") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeJ} << PowerPlugTypes{TypeF} << PowerPlugTypes{TypeJ}; 0221 QTest::newRow("CH-DE") << PowerPlugTypes{TypeC|TypeJ} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeJ} << PowerPlugTypes{TypeF}; 0222 QTest::newRow("DE-FR") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeE} << PowerPlugTypes{} << PowerPlugTypes{}; 0223 QTest::newRow("DE-GB") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeG} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeG}; 0224 QTest::newRow("DE-IT") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeF|TypeL} << PowerPlugTypes{} << PowerPlugTypes{TypeL}; 0225 QTest::newRow("IT-DE") << PowerPlugTypes{TypeC|TypeF|TypeL} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeL} << PowerPlugTypes{}; 0226 QTest::newRow("DE-IL") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeH|TypeM} << PowerPlugTypes{TypeF} << PowerPlugTypes{TypeH|TypeM}; 0227 QTest::newRow("DE-AO") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC} << PowerPlugTypes{TypeF} << PowerPlugTypes{}; 0228 QTest::newRow("AO-DE") << PowerPlugTypes{TypeC} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{} << PowerPlugTypes{}; 0229 QTest::newRow("DE-DK") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeE|TypeF|TypeK} << PowerPlugTypes{} << PowerPlugTypes{}; 0230 QTest::newRow("DK-DE") << PowerPlugTypes{TypeC|TypeF|TypeE|TypeK} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeK} << PowerPlugTypes{}; 0231 QTest::newRow("DE-ZA") << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeC|TypeD|TypeM|TypeN} << PowerPlugTypes{TypeF} << PowerPlugTypes{TypeD|TypeM|TypeN}; 0232 QTest::newRow("ZA-CH") << PowerPlugTypes{TypeC|TypeD|TypeM|TypeN} << PowerPlugTypes{TypeC|TypeJ} << PowerPlugTypes{TypeD|TypeM|TypeN} << PowerPlugTypes{TypeJ}; 0233 QTest::newRow("ZA-DE") << PowerPlugTypes{TypeC|TypeD|TypeM|TypeN} << PowerPlugTypes{TypeC|TypeF} << PowerPlugTypes{TypeD|TypeM|TypeN} << PowerPlugTypes{TypeF}; 0234 QTest::newRow("ZA-IT") << PowerPlugTypes{TypeC|TypeD|TypeM|TypeN} << PowerPlugTypes{TypeC|TypeF|TypeL} << PowerPlugTypes{TypeD|TypeM|TypeN} << PowerPlugTypes{TypeF|TypeL}; 0235 } 0236 0237 void testPowerPlugCompat() 0238 { 0239 using namespace KnowledgeDb; 0240 0241 QFETCH(PowerPlugTypes, plugs); 0242 QFETCH(PowerPlugTypes, sockets); 0243 QFETCH(PowerPlugTypes, failPlugs); 0244 QFETCH(PowerPlugTypes, failSockets); 0245 0246 QCOMPARE(KnowledgeDb::incompatiblePowerPlugs(plugs, sockets), failPlugs); 0247 QCOMPARE(KnowledgeDb::incompatiblePowerSockets(plugs, sockets), failSockets); 0248 } 0249 0250 void testTimezoneForCountry() 0251 { 0252 using namespace KnowledgeDb; 0253 0254 QCOMPARE(timezoneForLocation(NAN, NAN, u"DE", {}), QTimeZone("Europe/Berlin")); 0255 QCOMPARE(timezoneForLocation(NAN, NAN, u"FR", {}), QTimeZone("Europe/Paris")); 0256 QCOMPARE(timezoneForLocation(NAN, NAN, u"BR", {}), QTimeZone()); 0257 QCOMPARE(timezoneForLocation(NAN, NAN, u"US", {}), QTimeZone()); 0258 QCOMPARE(timezoneForLocation(NAN, NAN, u"US", u"CA"), QTimeZone("America/Los_Angeles")); 0259 } 0260 0261 void testTimezoneForLocation() 0262 { 0263 using namespace KnowledgeDb; 0264 0265 // basic checks in all quadrants 0266 QCOMPARE(timezoneForLocation(52.4, 13.1, {}, {}), QTimeZone("Europe/Berlin")); 0267 QCOMPARE(timezoneForLocation(-8.0, -35.0, {}, {}), QTimeZone("America/Recife")); 0268 QCOMPARE(timezoneForLocation(-36.5, 175.0, {}, {}), QTimeZone("Pacific/Auckland")); 0269 QCOMPARE(timezoneForLocation(44.0, -79.5, {}, {}), QTimeZone("America/Toronto")); 0270 0271 // Special case: Northern Vietnam has a Thai timezone 0272 QCOMPARE(timezoneForLocation(21.0, 106.0, {}, {}), QTimeZone("Asia/Bangkok")); 0273 0274 // Maastricht (NL), very close to the BE border 0275 QCOMPARE(timezoneForLocation(50.8505, 5.6881, QString(), {}), QTimeZone("Europe/Amsterdam")); 0276 QCOMPARE(timezoneForLocation(50.8505, 5.6881, u"NL", {}), QTimeZone("Europe/Amsterdam")); 0277 0278 // Aachen, at the BE/DE/NL corner 0279 QCOMPARE(timezoneForLocation(50.7717, 6.04235, QString(), {}), QTimeZone("Europe/Berlin")); 0280 QCOMPARE(timezoneForLocation(50.7717, 6.04235, u"DE", {}), QTimeZone("Europe/Berlin")); 0281 //QCOMPARE(timezoneForLocation(50.7727, 6.01565, QString()), QTimeZone("Europe/Brussels")); 0282 QCOMPARE(timezoneForLocation(50.7727, 6.01565, u"BE", {}), QTimeZone("Europe/Brussels")); 0283 0284 // Geneva (CH), very close to the FR border 0285 QCOMPARE(timezoneForLocation(46.23213, 6.10636, u"CH", {}), QTimeZone("Europe/Zurich")); 0286 0287 // Busingen (DE), enclosed by CH, and in theory its own timezone (which we ignore) 0288 QCOMPARE(timezoneForLocation(47.69947, 8.68833, u"DE", {}), QTimeZone("Europe/Berlin")); 0289 QCOMPARE(timezoneForLocation(47.67904, 8.68813, {}, {}), QTimeZone("Europe/Zurich")); 0290 0291 // Baarle, the ultimate special case, NL/BE differs house by house 0292 QCOMPARE(timezoneForLocation(51.44344, 4.93373, u"BE", {}), QTimeZone("Europe/Brussels")); 0293 QCOMPARE(timezoneForLocation(51.44344, 4.93373, u"NL", {}), QTimeZone("Europe/Amsterdam")); 0294 const auto tz = timezoneForLocation(51.44344, 4.93373, {}, {}); 0295 QVERIFY(tz == QTimeZone("Europe/Amsterdam") || tz == QTimeZone("Europe/Brussels")); 0296 0297 // Eliat Airport (IL), close to JO, and with a minor timezone variation due to different weekends 0298 QCOMPARE(timezoneForLocation(29.72530, 35.00598, u"IL", {}), QTimeZone("Asia/Jerusalem")); 0299 QCOMPARE(timezoneForLocation(29.60908, 35.02038, u"JO", {}), QTimeZone("Asia/Amman")); 0300 0301 // Tijuana (MX), close to US, tests equivalent tz search in the neighbouring country 0302 QCOMPARE(timezoneForLocation(32.54274, -116.97505, u"MX", {}), QTimeZone("America/Tijuana")); 0303 QCOMPARE(timezoneForLocation(32.55783, -117.04773, u"US", {}), QTimeZone("America/Los_Angeles")); 0304 0305 // Cordoba (AR), AR has several sub-zones that are all equivalent 0306 QCOMPARE(timezoneForLocation(-31.4, -64.2, u"AR", {}), QTimeZone("America/Argentina/Buenos_Aires")); 0307 0308 // polar regions 0309 QCOMPARE(timezoneForLocation(-90.0, 0.0, {}, {}), QTimeZone()); 0310 QCOMPARE(timezoneForLocation(90.0, 0.0, {}, {}), QTimeZone()); 0311 0312 // Hong Kong seems problematic on FreeBSD 0313 QCOMPARE(timezoneForLocation(22.31600, 113.93688, {}, {}), QTimeZone("Asia/Hong_Kong")); 0314 0315 // coordinates not provided 0316 QCOMPARE(timezoneForLocation(NAN, NAN, u"LU", {}), QTimeZone("Europe/Luxembourg")); 0317 } 0318 0319 void testUICCountryCodeLookup() 0320 { 0321 using namespace KnowledgeDb; 0322 0323 QCOMPARE(KnowledgeDb::countryIdForUicCode(80), CountryId{"DE"}); 0324 QCOMPARE(KnowledgeDb::countryIdForUicCode(0), CountryId{}); 0325 } 0326 0327 void testIndianRailwaysStationCodeLookup() 0328 { 0329 auto station = KnowledgeDb::stationForIndianRailwaysStationCode(QString()); 0330 QVERIFY(!station.coordinate.isValid()); 0331 0332 station = KnowledgeDb::stationForIndianRailwaysStationCode(QStringLiteral("NDLS")); 0333 QVERIFY(station.coordinate.isValid()); 0334 QCOMPARE(station.country, CountryId{"IN"}); 0335 0336 station = KnowledgeDb::stationForIndianRailwaysStationCode(QStringLiteral("ndls")); 0337 QVERIFY(!station.coordinate.isValid()); 0338 } 0339 0340 void testFinishStationCodeLookup() 0341 { 0342 auto station = KnowledgeDb::stationForVRStationCode(VRStationCode(QStringLiteral("HKI"))); 0343 QVERIFY(station.coordinate.isValid()); 0344 0345 station = KnowledgeDb::stationForVRStationCode(VRStationCode(QStringLiteral("BLĂ„"))); 0346 QVERIFY(!station.coordinate.isValid()); 0347 } 0348 }; 0349 0350 QTEST_APPLESS_MAIN(KnowledgeDbTest) 0351 0352 #include "knowledgedbtest.moc"