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 "testhelpers.h"
0008 
0009 #include <KItinerary/Flight>
0010 #include <KItinerary/JsonLdDocument>
0011 #include <KItinerary/MergeUtil>
0012 #include <KItinerary/Organization>
0013 #include <KItinerary/Person>
0014 #include <KItinerary/Reservation>
0015 #include <KItinerary/TrainTrip>
0016 
0017 #include <QDateTime>
0018 #include <QDebug>
0019 #include <QFile>
0020 #include <QJsonArray>
0021 #include <QJsonDocument>
0022 #include <QJsonObject>
0023 #include <QObject>
0024 #include <QTest>
0025 
0026 #define _(x) QStringLiteral(x)
0027 
0028 using namespace KItinerary;
0029 
0030 class MergeUtilTest : public QObject
0031 {
0032     Q_OBJECT
0033 private:
0034     QByteArray readFile(const QString &fn) const
0035     {
0036         QFile f(fn);
0037         f.open(QFile::ReadOnly);
0038         return f.readAll();
0039     }
0040 
0041 private Q_SLOTS:
0042     void testIsSameReservation()
0043     {
0044         QVERIFY(!MergeUtil::isSame({}, {}));
0045         FlightReservation res1;
0046         QVERIFY(!MergeUtil::isSame(res1, {}));
0047         QVERIFY(!MergeUtil::isSame({}, res1));
0048 
0049         res1.setReservationNumber(QStringLiteral("XXX007"));
0050         Flight flight1;
0051         flight1.setFlightNumber(QStringLiteral("1234"));
0052         flight1.setDepartureDay(QDate(2018, 4, 21));
0053         res1.setReservationFor(flight1);
0054 
0055         FlightReservation res2;
0056         res2.setReservationNumber(QStringLiteral("YYY008"));
0057         Flight flight2;
0058         flight2.setFlightNumber(QStringLiteral("1234"));
0059         res2.setReservationFor(flight2);
0060         QVERIFY(!MergeUtil::isSame(res1, res2));
0061 
0062         flight2.setDepartureDay(QDate(2018, 4, 21));
0063         res2.setReservationFor(flight2);
0064         QVERIFY(MergeUtil::isSame(flight2, res2.reservationFor()));
0065         QVERIFY(!MergeUtil::isSame(res1, res2));
0066 
0067         res2.setReservationNumber(QStringLiteral("XXX007"));
0068         QVERIFY(MergeUtil::isSame(flight1, flight2));
0069         QVERIFY(MergeUtil::isSame(res1.reservationFor(), res2.reservationFor()));
0070         QVERIFY(MergeUtil::isSame(res1, res2));
0071     }
0072 
0073     void testIsSameFlight()
0074     {
0075         Airline airline1;
0076         airline1.setIataCode(QStringLiteral("KL"));
0077         Flight f1;
0078         f1.setAirline(airline1);
0079         f1.setFlightNumber(QStringLiteral("8457"));
0080         f1.setDepartureTime(QDateTime(QDate(2018, 4, 2), QTime(17, 51, 0)));
0081 
0082         Flight f2;
0083         QVERIFY(!MergeUtil::isSame(f1, f2));
0084 
0085         f2.setFlightNumber(QStringLiteral("8457"));
0086         QVERIFY(!MergeUtil::isSame(f1, f2));
0087 
0088         Airline airline2;
0089         airline2.setIataCode(QStringLiteral("AF"));
0090         f2.setAirline(airline2);
0091         QVERIFY(!MergeUtil::isSame(f1, f2));
0092         airline2.setIataCode(QStringLiteral("KL"));
0093         f2.setAirline(airline2);
0094         QVERIFY(MergeUtil::isSame(f1, f2));
0095 
0096         f2.setDepartureDay(QDate(2018, 4, 2));
0097         QVERIFY(MergeUtil::isSame(f1, f2));
0098         f2.setDepartureDay(QDate(2018, 4, 3));
0099         QVERIFY(!MergeUtil::isSame(f1, f2));
0100     }
0101 
0102     void testCodeShareFlight()
0103     {
0104         Airline a1;
0105         a1.setIataCode(QStringLiteral("4U"));
0106         Flight f1;
0107         f1.setAirline(a1);
0108         f1.setFlightNumber(QStringLiteral("42"));
0109         f1.setDepartureDay(QDate(2018, 04, 21));
0110 
0111         Airline a2;
0112         a2.setIataCode(QStringLiteral("EW"));
0113         Flight f2(f1);
0114         f2.setAirline(a2);
0115 
0116         QVERIFY(MergeUtil::isSame(f1, f2));
0117     }
0118 
0119     void testIsSamePerson_data()
0120     {
0121         // we do not need to consider cases here that ExtractorPostprocessor eliminates for us
0122         // such as filling the full name or honoric prefixes
0123         QTest::addColumn<QList<QStringList>>("data");
0124 
0125         QTest::newRow("simple name") << QList<QStringList>{
0126             {_("Volker Krause"), {}, {}},
0127             {_("VOLKER KRAUSE"), {}, {}},
0128             {_("VOLKER KRAUSE"), _("Volker"), _("Krause")},
0129             {_("VOLKER KRAUSE"), {}, _("Krause")},
0130             {_("VOLKER KRAUSE"), _("Volker"), {}},
0131             // IATA BCBP artifacts
0132             {_("VOLKERMR KRAUSE"), _("VOLKERMR"), _("KRAUSE")},
0133             {_("VOLKER MR KRAUSE"), _("VOLKER MR"), _("KRAUSE")}};
0134 
0135         QTest::newRow("double family name") << QList<QStringList>{
0136             {_("Andreas Cord-Landwehr"), {}, {}},
0137             {_("ANDREAS CORD-LANDWEHR"), {}, {}},
0138             {_("ANDREAS CORD-LANDWEHR"), _("Andreas"), _("Cord-Landwehr")},
0139             // IATA BCBP artifacts
0140             {_("Andreas Cordlandwehr"), {}, {}},
0141             {_("ANDREAS CORDLANDWEHR"), _("ANDREAS"), _("CORDLANDWEHR")},
0142             {_("ANDREAS CORD LANDWEHR"), _("ANDREAS"), _("CORD LANDWEHR")},
0143             {_("ANDREAS CORD LANDWEHR"), {}, {}}};
0144 
0145         QTest::newRow("diacritic") << QList<QStringList>{
0146             {_("Daniel Vrátil"), {}, {}},
0147             {_("Daniel Vrátil"), _("Daniel"), _("Vrátil")},
0148             {_("DANIEL VRATIL"), {}, {}},
0149             {_("DANIEL VRATIL"), _("DANIEL"), _("VRATIL")}};
0150 
0151         QTest::newRow("transliteration") << QList<QStringList>{
0152             {_("Bjärn Lastname"), {}, {}},
0153             {_("BJAERN LASTNAME"), _("BJAERN"), _("LASTNAME")},
0154         };
0155 
0156         QTest::newRow("partial") << QList<QStringList> {
0157             {_("VOLKER KRAUSE"), _("Volker"), _("Krause")},
0158             {_("KRAUSE"),{}, {}}
0159         };
0160 
0161         QTest::newRow("double given name") << QList<QStringList> {
0162             {_("Kai Uwe Broulik"), _("Kai Uwe"), _("Broulik")},
0163             {_("Kai Broulik"), _("Kai"), _("Broulik")},
0164         };
0165     }
0166 
0167     void testIsSamePerson()
0168     {
0169         QFETCH(QList<QStringList>, data);
0170 
0171         for(int i = 0; i < data.size(); ++i) {
0172             Person lhs;
0173             lhs.setName(data[i][0]);
0174             lhs.setGivenName(data[i][1]);
0175             lhs.setFamilyName(data[i][2]);
0176 
0177             for (int j = 0; j < data.size(); ++j) {
0178                 Person rhs;
0179                 rhs.setName(data[j][0]);
0180                 rhs.setGivenName(data[j][1]);
0181                 rhs.setFamilyName(data[j][2]);
0182 
0183                 QVERIFY(!MergeUtil::isSamePerson(lhs, {}));
0184                 QVERIFY(!MergeUtil::isSamePerson({}, lhs));
0185 
0186                 if (!MergeUtil::isSamePerson(lhs, rhs)) {
0187                     qDebug() << "Left: " << lhs.name() << lhs.givenName() << lhs.familyName();
0188                     qDebug() << "Right: " << rhs.name() << rhs.givenName() << rhs.familyName();
0189                 }
0190                 QVERIFY(MergeUtil::isSamePerson(lhs, rhs));
0191             }
0192         }
0193     }
0194 
0195     void testIsNotSamePerson()
0196     {
0197         QList<QStringList> data{
0198             {_("Volker Krause"), {}, {}},
0199             {_("Andreas Cord-Landwehr"), _("Andread"), _("Cord-Landwehr")},
0200             {_("GIVEN1 GIVEN2 FAMILY1"), {}, {}},
0201             {_("V K"), {}, {}},
0202             {_("Daniel Vrátil"), _("Daniel"), _("Vrátil")},
0203         };
0204 
0205         for(int i = 0; i < data.size(); ++i) {
0206             Person lhs;
0207             lhs.setName(data[i][0]);
0208             lhs.setGivenName(data[i][1]);
0209             lhs.setFamilyName(data[i][2]);
0210 
0211             for (int j = 0; j < data.size(); ++j) {
0212                 if (i == j) {
0213                     continue;
0214                 }
0215 
0216                 Person rhs;
0217                 rhs.setName(data[j][0]);
0218                 rhs.setGivenName(data[j][1]);
0219                 rhs.setFamilyName(data[j][2]);
0220 
0221                 QVERIFY(!MergeUtil::isSamePerson(lhs, {}));
0222                 QVERIFY(!MergeUtil::isSamePerson({}, lhs));
0223 
0224                 if (MergeUtil::isSamePerson(lhs, rhs)) {
0225                     qDebug() << "Left: " << lhs.name() << lhs.givenName() << lhs.familyName();
0226                     qDebug() << "Right: " << rhs.name() << rhs.givenName() << rhs.familyName();
0227                 }
0228                 QVERIFY(!MergeUtil::isSamePerson(lhs, rhs));
0229             }
0230         }
0231     }
0232 
0233     void testIsSameTrain_data()
0234     {
0235         QTest::addColumn<QString>("lhsName");
0236         QTest::addColumn<QString>("lhsNumber");
0237         QTest::addColumn<QString>("rhsName");
0238         QTest::addColumn<QString>("rhsNumber");
0239         QTest::addColumn<bool>("shouldBeSame");
0240 
0241         QTest::newRow("empty") << QString() << QString() << QString() << QString() << false;
0242         QTest::newRow("equal number") << QString() << _("123") << QString() << _("123") << true;
0243         QTest::newRow("optional name") << QString() << _("123") << _("EC") << _("123") << true;
0244         QTest::newRow("optional name2") << QString() << _("123") << QString() << _("EC 123") << true;
0245         QTest::newRow("name mismatch") << _("IC") << _("123") << _("EC") << _("123") << false;
0246         QTest::newRow("name mismatch2") << QString() << _("IC 123") << QString() << _("EC 123") << false;
0247         QTest::newRow("number mismatch") << QString() << _("123") << QString() << _("234") << false;
0248         QTest::newRow("number prefix") << QString() << _("123") << QString() << _("12") << false;
0249         QTest::newRow("number prefix2") << QString() << _("123") << QString() << _("23") << false;
0250 
0251         QTest::newRow("train-and-line-number-0") << QString() << _("RE 13 (20070)") << QString() << _("RE 13 (20072)") << false;
0252         QTest::newRow("train-and-line-number-1") << QString() << _("RE 13 (20070)") << QString() << _("RE 20070") << true;
0253         QTest::newRow("train-and-line-number-2") << QString() << _("RE 13 (20070)") << QString() << _("RE 13") << true;
0254         //QTest::newRow("train-and-line-number-3") << QString() << _("RE 13 (20070)") << QString() << _("RE") <<  false;
0255         QTest::newRow("train-and-line-number-4") << QString() << _("RB 33 (10323)") << QString() << _("RE 33") << false;
0256         QTest::newRow("train-and-line-number-5") << QString() << _("RB 33 (10323)") << QString() << _("RE 10323") << false;
0257         QTest::newRow("train-and-line-number-6") << QString() << _("RB 33 (10323)") << QString() << _("RB 10323") << true;
0258         QTest::newRow("train-and-line-number-7") << QString() << _("RB 33 (10323)") << QString() << _("RB 33") << true;
0259         QTest::newRow("train-and-line-number-8") << QString() << _("RB 33 (10323)") << QString() << _("RB 23") << false;
0260         QTest::newRow("train-and-line-number-9") << QString() << _("RB 33 (10323)") << QString() << _("RB 23 (10323)") << false;
0261     }
0262 
0263     void testIsSameTrain()
0264     {
0265         QFETCH(QString, lhsName);
0266         QFETCH(QString, lhsNumber);
0267         QFETCH(QString, rhsName);
0268         QFETCH(QString, rhsNumber);
0269         QFETCH(bool, shouldBeSame);
0270 
0271         TrainTrip lhs;
0272         lhs.setDepartureTime(QDateTime({2019, 11, 9}, {12, 00}));
0273         lhs.setTrainName(lhsName);
0274         lhs.setTrainNumber(lhsNumber);
0275 
0276         TrainTrip rhs;
0277         rhs.setDepartureTime(QDateTime({2019, 11, 9}, {12, 00}));
0278         rhs.setTrainName(rhsName);
0279         rhs.setTrainNumber(rhsNumber);
0280 
0281         QCOMPARE(MergeUtil::isSame(lhs, rhs), shouldBeSame);
0282         QCOMPARE(MergeUtil::isSame(rhs, lhs), shouldBeSame);
0283     }
0284 
0285     void testIsSameLodingReservation()
0286     {
0287         LodgingReservation res1;
0288         LodgingBusiness hotel1;
0289         hotel1.setName(QStringLiteral("Haus Randa"));
0290         res1.setReservationFor(hotel1);
0291         res1.setCheckinTime(QDateTime(QDate(2018, 4, 9), QTime(10, 0)));
0292         res1.setReservationNumber(QStringLiteral("1234"));
0293 
0294         LodgingReservation res2;
0295         QVERIFY(!MergeUtil::isSame(res1, res2));
0296         res2.setReservationNumber(QStringLiteral("1234"));
0297         QVERIFY(!MergeUtil::isSame(res1, res2));
0298         res2.setCheckinTime(QDateTime(QDate(2018, 4, 9), QTime(15, 0)));
0299         QVERIFY(!MergeUtil::isSame(res1, res2));
0300         LodgingBusiness hotel2;
0301         hotel2.setName(QStringLiteral("Haus Randa"));
0302         res2.setReservationFor(hotel2);
0303         QVERIFY(MergeUtil::isSame(res1, res2));
0304     }
0305 
0306     void testIsSameCancellation()
0307     {
0308         const auto lhs = JsonLdDocument::fromJson(QJsonDocument::fromJson(readFile(_(SOURCE_DIR "/mergedata/cancellation.lhs.json"))).array());
0309         const auto rhs = JsonLdDocument::fromJson(QJsonDocument::fromJson(readFile(_(SOURCE_DIR "/mergedata/cancellation.rhs.json"))).array());
0310         QCOMPARE(lhs.size(), 1);
0311         QCOMPARE(rhs.size(), 1);
0312         QVERIFY(MergeUtil::isSame(lhs[0], rhs[0]));
0313     }
0314 
0315     void testMerge_data()
0316     {
0317         QTest::addColumn<QString>("baseName");
0318 
0319         QDir dir(QStringLiteral(SOURCE_DIR "/mergedata"));
0320         const auto lst = dir.entryList(QStringList(QStringLiteral("*.merged.json")), QDir::Files | QDir::Readable | QDir::NoSymLinks);
0321         for (const auto &file : lst) {
0322             const auto base = file.left(file.size() - 12);
0323             QTest::newRow(base.toLatin1().constData()) << (dir.path() + QLatin1Char('/') + base);
0324         }
0325     }
0326 
0327     void testMerge()
0328     {
0329         QFETCH(QString, baseName);
0330 
0331         const auto lhs =
0332             JsonLdDocument::fromJson(
0333                 QJsonDocument::fromJson(
0334                     readFile(baseName + QLatin1StringView(".lhs.json")))
0335                     .array())
0336                 .first();
0337         const auto rhs =
0338             JsonLdDocument::fromJson(
0339                 QJsonDocument::fromJson(
0340                     readFile(baseName + QLatin1StringView(".rhs.json")))
0341                     .array())
0342                 .first();
0343         const auto expected = QJsonDocument::fromJson(
0344             readFile(baseName + QLatin1StringView(".merged.json")));
0345 
0346         const auto mergedL2R = MergeUtil::merge(lhs, rhs);
0347         auto mergedJson =
0348             QJsonDocument(JsonLdDocument::toJson(QList<QVariant>({mergedL2R})));
0349         if (mergedJson != expected) {
0350           Test::compareJson(baseName + QLatin1StringView(".merged.json"),
0351                             mergedJson, expected);
0352         }
0353         QCOMPARE(mergedJson, expected);
0354 
0355         const auto mergedR2L = MergeUtil::merge(rhs, lhs);
0356         mergedJson =
0357             QJsonDocument(JsonLdDocument::toJson(QList<QVariant>({mergedR2L})));
0358         if (mergedJson != expected) {
0359           Test::compareJson(baseName + QLatin1StringView(".merged.json"),
0360                             mergedJson, expected);
0361         }
0362         QCOMPARE(mergedJson, expected);
0363     }
0364 
0365     void testIsSameIncidence()
0366     {
0367       const auto lhsFlight =
0368           JsonLdDocument::fromJson(
0369               QJsonDocument::fromJson(
0370                   readFile(QLatin1StringView(
0371                       SOURCE_DIR "/calendarhandlerdata/flight.json")))
0372                   .array())
0373               .first()
0374               .value<FlightReservation>();
0375       auto rhsFlight = lhsFlight;
0376       Person p = rhsFlight.underName().value<Person>();
0377       p.setName(QLatin1StringView("Jane Doe"));
0378       rhsFlight.setUnderName(p);
0379       QVERIFY(!MergeUtil::isSame(lhsFlight, rhsFlight));
0380       QVERIFY(MergeUtil::isSameIncidence(lhsFlight, rhsFlight));
0381 
0382       const auto lhsHotel =
0383           JsonLdDocument::fromJson(
0384               QJsonDocument::fromJson(
0385                   readFile(QLatin1StringView(
0386                       SOURCE_DIR "/calendarhandlerdata/hotel.json")))
0387                   .array())
0388               .first()
0389               .value<LodgingReservation>();
0390       auto rhsHotel = lhsHotel;
0391       p = rhsHotel.underName().value<Person>();
0392       p.setName(QLatin1StringView("Jane Doe"));
0393       rhsHotel.setUnderName(p);
0394       QVERIFY(!MergeUtil::isSame(lhsHotel, rhsHotel));
0395       QVERIFY(MergeUtil::isSameIncidence(lhsHotel, rhsHotel));
0396       rhsHotel.setCheckinTime(rhsHotel.checkoutTime().addDays(2));
0397       QVERIFY(!MergeUtil::isSame(lhsHotel, rhsHotel));
0398       QVERIFY(!MergeUtil::isSameIncidence(lhsHotel, rhsHotel));
0399     }
0400 };
0401 
0402 QTEST_APPLESS_MAIN(MergeUtilTest)
0403 
0404 #include "mergeutiltest.moc"