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"