File indexing completed on 2024-05-12 05:13:45
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "testhelper.h" 0008 #include "mocknetworkaccessmanager.h" 0009 0010 #include "livedata.h" 0011 #include "livedatamanager.h" 0012 #include "applicationcontroller.h" 0013 #include "reservationmanager.h" 0014 0015 #include <KItinerary/Reservation> 0016 #include <KItinerary/TrainTrip> 0017 0018 #include <KPublicTransport/Manager> 0019 0020 #include <QJsonDocument> 0021 #include <QJsonObject> 0022 #include <QtTest/qtest.h> 0023 #include <QSignalSpy> 0024 #include <QStandardPaths> 0025 #include <QTimeZone> 0026 0027 #define s(x) QStringLiteral(x) 0028 0029 using namespace KItinerary; 0030 0031 static MockNetworkAccessManager s_nam; 0032 static QNetworkAccessManager* namFactory() { return &s_nam; } 0033 0034 class LiveDataManagerTest : public QObject 0035 { 0036 Q_OBJECT 0037 private Q_SLOTS: 0038 void initTestCase() 0039 { 0040 qputenv("TZ", "UTC"); 0041 QStandardPaths::setTestModeEnabled(true); 0042 } 0043 0044 void testPersistence() 0045 { 0046 LiveData::clearStorage(); 0047 QCOMPARE(LiveData::listAll(), std::vector<QString>()); 0048 0049 { 0050 LiveData ld; 0051 ld.departure.setScheduledDepartureTime({{2017, 9, 10}, {11, 0}}); 0052 ld.departureTimestamp = {{ 2017, 1, 1} , {0, 0}}; 0053 ld.store(s("testId"), LiveData::Departure); 0054 } 0055 0056 QCOMPARE(LiveData::listAll(), std::vector<QString>({ s("testId") })); 0057 0058 { 0059 auto ld = LiveData::load(s("testId")); 0060 QCOMPARE(ld.departure.scheduledDepartureTime(), QDateTime({2017, 9, 10}, {11, 0})); 0061 QCOMPARE(ld.departureTimestamp, QDateTime({2017, 1, 1}, {0, 0})); 0062 QVERIFY(!ld.arrivalTimestamp.isValid()); 0063 ld.departure = {}; 0064 ld.store(s("testId"), LiveData::AllTypes); 0065 } 0066 0067 QCOMPARE(LiveData::listAll(), std::vector<QString>()); 0068 } 0069 0070 void testLiveData() 0071 { 0072 ReservationManager resMgr; 0073 PkPassManager pkPassMgr; 0074 Test::clearAll(&resMgr); 0075 QSignalSpy resChangeSpy(&resMgr, &ReservationManager::batchContentChanged); 0076 LiveData::clearStorage(); 0077 0078 LiveDataManager ldm; 0079 ldm.setPkPassManager(&pkPassMgr); 0080 QSignalSpy arrivalUpdateSpy(&ldm, &LiveDataManager::arrivalUpdated); 0081 QSignalSpy departureUpdateSpy(&ldm, &LiveDataManager::departureUpdated); 0082 ldm.setPollingEnabled(false); // we don't want to trigger network requests here 0083 QVERIFY(ldm.publicTransportManager()); 0084 ldm.m_unitTestTime = QDateTime({2017, 9, 10}, {12, 0}, QTimeZone("Europe/Zurich")); // that's in the middle of the first train leg 0085 ldm.setReservationManager(&resMgr); 0086 0087 auto ctrl = Test::makeAppController(); 0088 ctrl->setReservationManager(&resMgr); 0089 ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/../tests/randa2017.json"))); 0090 QCOMPARE(resMgr.batches().size(), 11); 0091 0092 const auto flight = resMgr.batches()[0]; 0093 QVERIFY(!ldm.isRelevant(flight)); 0094 QVERIFY(ldm.hasDeparted(flight, resMgr.reservation(flight))); 0095 QVERIFY(ldm.hasArrived(flight, resMgr.reservation(flight))); 0096 0097 const auto trainLeg1 = resMgr.batches()[1]; 0098 QVERIFY(ldm.isRelevant(trainLeg1)); 0099 QVERIFY(ldm.hasDeparted(trainLeg1, resMgr.reservation(trainLeg1))); 0100 QVERIFY(!ldm.hasArrived(trainLeg1, resMgr.reservation(trainLeg1))); 0101 0102 const auto trainLeg2 = resMgr.batches()[2]; 0103 QVERIFY(ldm.isRelevant(trainLeg2)); 0104 QVERIFY(!ldm.hasDeparted(trainLeg2, resMgr.reservation(trainLeg2))); 0105 QVERIFY(!ldm.hasArrived(trainLeg2, resMgr.reservation(trainLeg2))); 0106 0107 QCOMPARE(ldm.nextPollTimeForReservation(flight), std::numeric_limits<int>::max()); 0108 QCOMPARE(ldm.nextPollTimeForReservation(trainLeg1), 0); // no current data available, so we want to poll ASAP 0109 QCOMPARE(ldm.nextPollTimeForReservation(trainLeg2), 0); 0110 QTest::qWait(0); 0111 QCOMPARE(ldm.nextPollTime(), 0); 0112 QCOMPARE(resMgr.reservation(trainLeg1).value<TrainReservation>().reservationFor().value<TrainTrip>().arrivalStation().address().addressLocality(), QString()); 0113 0114 const auto leg1Arr = KPublicTransport::Stopover::fromJson(QJsonDocument::fromJson(Test::readFile(s(SOURCE_DIR "/data/livedata/randa2017-leg1-arrival.json"))).object()); 0115 ldm.stopoverQueryFinished({ leg1Arr }, LiveData::Arrival, trainLeg1); 0116 QCOMPARE(arrivalUpdateSpy.size(), 1); 0117 QCOMPARE(arrivalUpdateSpy.at(0).at(0).toString(), trainLeg1); 0118 QCOMPARE(departureUpdateSpy.size(), 0); 0119 QCOMPARE(ldm.arrival(trainLeg1).arrivalDelay(), 2); 0120 QCOMPARE(ldm.nextPollTimeForReservation(trainLeg1), 15 * 60 * 1000); // 15 min in msecs 0121 // reservation was updated with additional location data 0122 QCOMPARE(resChangeSpy.size(), 1); 0123 QCOMPARE(resChangeSpy.at(0).at(0).toString(), trainLeg1); 0124 QCOMPARE(resMgr.reservation(trainLeg1).value<TrainReservation>().reservationFor().value<TrainTrip>().arrivalStation().address().addressLocality(), QLatin1StringView("Visp")); 0125 0126 // verify this was persisted 0127 { 0128 LiveDataManager ldm2; 0129 QCOMPARE(ldm.arrival(trainLeg1).arrivalDelay(), 2); 0130 } 0131 0132 // failed lookups are recorded to avoid a polling loop 0133 ldm.stopoverQueryFinished({ leg1Arr }, LiveData::Departure, trainLeg2); 0134 ldm.stopoverQueryFinished({ leg1Arr }, LiveData::Arrival, trainLeg2); 0135 QCOMPARE(ldm.departure(trainLeg2).stopPoint().isEmpty(), true); 0136 QCOMPARE(ldm.nextPollTimeForReservation(trainLeg2), 15 * 60 * 1000); 0137 } 0138 0139 void testPkPassUpdate() 0140 { 0141 PkPassManager pkPassMgr; 0142 QSignalSpy passUpdateSpy(&pkPassMgr, &PkPassManager::passUpdated); 0143 pkPassMgr.setNetworkAccessManagerFactory(namFactory); 0144 Test::clearAll(&pkPassMgr); 0145 ReservationManager resMgr; 0146 Test::clearAll(&resMgr); 0147 0148 LiveData::clearStorage(); 0149 LiveDataManager ldm; 0150 ldm.setPkPassManager(&pkPassMgr); 0151 ldm.setPollingEnabled(true); 0152 ldm.m_unitTestTime = QDateTime({2023, 7, 14}, {15, 0, 0}, QTimeZone("Europe/Berlin")); 0153 ldm.setReservationManager(&resMgr); 0154 0155 auto ctrl = Test::makeAppController(); 0156 ctrl->setPkPassManager(&pkPassMgr); 0157 ctrl->setReservationManager(&resMgr); 0158 ctrl->importFromUrl(QUrl::fromLocalFile(QLatin1StringView(SOURCE_DIR "/data/updateable-boardingpass.pkpass"))); 0159 0160 QCOMPARE(resMgr.batches().size(), 1); 0161 const auto resId = resMgr.batches()[0]; 0162 QCOMPARE(ldm.isRelevant(resId), true); 0163 QCOMPARE(ldm.nextPollTimeForReservation(resId), 0); 0164 QTest::qWait(0); 0165 0166 QCOMPARE(pkPassMgr.passes().size(), 1); 0167 const auto pass = pkPassMgr.pass(pkPassMgr.passes()[0]); 0168 QVERIFY(pass); 0169 QVERIFY(PkPassManager::canUpdate(pass)); 0170 0171 QCOMPARE(s_nam.requests.size(), 1); 0172 QTest::qWait(0); // download failed 0173 QCOMPARE(passUpdateSpy.size(), 0); 0174 QVERIFY(ldm.nextPollTimeForReservation(resId) > 0); 0175 QVERIFY(ldm.nextPollTimeForReservation(resId) <= 30000); 0176 QVERIFY(ldm.pollCooldown(resId) > 0); 0177 QVERIFY(ldm.pollCooldown(resId) <= 30000); 0178 QVERIFY(ldm.nextPollTime() > 0); 0179 QVERIFY(ldm.nextPollTime() <= 30000); 0180 0181 ldm.m_unitTestTime = QDateTime({2023, 7, 14}, {15, 5, 0}, QTimeZone("Europe/Berlin")); 0182 QCOMPARE(ldm.nextPollTimeForReservation(resId), 0); 0183 QCOMPARE(ldm.pollCooldown(resId), 0); 0184 QCOMPARE(ldm.nextPollTime(), 0); 0185 } 0186 }; 0187 0188 QTEST_GUILESS_MAIN(LiveDataManagerTest) 0189 0190 #include "livedatamanagertest.moc"