File indexing completed on 2024-05-12 05:17:29

0001 /*
0002   SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "calendarhandler.h"
0008 #include "extractorpostprocessor.h"
0009 #include "jsonlddocument.h"
0010 
0011 #include <KCalendarCore/ICalFormat>
0012 #include <KCalendarCore/MemoryCalendar>
0013 
0014 #include <QDebug>
0015 #include <QFile>
0016 #include <QJsonArray>
0017 #include <QJsonDocument>
0018 #include <QProcess>
0019 #include <QObject>
0020 #include <QTest>
0021 
0022 using namespace KCalendarCore;
0023 using namespace KItinerary;
0024 
0025 void initLocale()
0026 {
0027     qputenv("LC_ALL", "en_US.utf-8");
0028     qputenv("TZ", "UTC");
0029 }
0030 
0031 Q_CONSTRUCTOR_FUNCTION(initLocale)
0032 
0033 class CalendarHandlerTest : public QObject
0034 {
0035     Q_OBJECT
0036 private Q_SLOTS:
0037     void testCreateEvent_data()
0038     {
0039         QTest::addColumn<QString>("jsonFile");
0040         QTest::addColumn<QString>("icalFile");
0041 
0042         QDir dir(QStringLiteral(SOURCE_DIR "/calendarhandlerdata"));
0043         const auto lst = dir.entryList(QStringList(QStringLiteral("*.json")), QDir::Files | QDir::Readable | QDir::NoSymLinks);
0044         for (const auto &file : lst) {
0045             const QString refFile = dir.path() + QLatin1Char('/') + file.left(file.size() - 4) + QStringLiteral("ics");
0046             if (!QFile::exists(refFile)) {
0047                 qDebug() << "reference file" << refFile << "does not exist, skipping test file" << file;
0048                 continue;
0049             }
0050             QTest::newRow(file.toLatin1().constData()) << QString(dir.path() + QLatin1Char('/') +  file) << refFile;
0051         }
0052     }
0053 
0054     void testCreateEvent()
0055     {
0056         QFETCH(QString, jsonFile);
0057         QFETCH(QString, icalFile);
0058 
0059         QFile f(jsonFile);
0060         QVERIFY(f.open(QFile::ReadOnly));
0061         const auto inArray = QJsonDocument::fromJson(f.readAll()).array();
0062         QVERIFY(!inArray.isEmpty());
0063         const auto preData = JsonLdDocument::fromJson(inArray);
0064         QCOMPARE(inArray.size(), preData.size());
0065 
0066         ExtractorPostprocessor postproc;
0067         postproc.process(preData);
0068         const auto postData = postproc.result();
0069         QVERIFY(std::all_of(postData.begin(), postData.end(), CalendarHandler::canCreateEvent));
0070         QCOMPARE(inArray.size(), postData.size());
0071 
0072         MemoryCalendar::Ptr refCal(new MemoryCalendar(QTimeZone::systemTimeZone()));
0073         ICalFormat format;
0074         QVERIFY(format.load(refCal, icalFile));
0075 
0076         const auto refEvents = refCal->rawEvents(KCalendarCore::EventSortStartDate, KCalendarCore::SortDirectionAscending);
0077         QCOMPARE(refEvents.size(), 1);
0078         Event::Ptr newEvent(new Event);
0079         CalendarHandler::fillEvent(postData, newEvent);
0080 
0081         // sync volatile fields, we only care for differences elsewhere
0082         const auto &refEvent = refEvents.at(0);
0083         newEvent->setUid(refEvent->uid());
0084         newEvent->setLastModified(refEvent->lastModified());
0085         newEvent->setCreated(refEvent->created());
0086         // normalize the effect of CLDR 42
0087         newEvent->setDescription(newEvent->description().replace(
0088             QStringLiteral("\u202F"), QLatin1StringView(" ")));
0089 
0090         if (*newEvent != *refEvent) {
0091             qDebug().noquote() << "Actual: " << format.toICalString(newEvent).remove(QLatin1Char('\r'));
0092             qDebug().noquote() << "Expected: " << format.toICalString(refEvent).remove(QLatin1Char('\r'));
0093 
0094             QFile failFile(icalFile + QLatin1StringView(".fail"));
0095             QVERIFY(failFile.open(QFile::WriteOnly));
0096             failFile.write(format.toICalString(newEvent).remove(QLatin1Char('\r')).toUtf8());
0097             failFile.close();
0098 
0099             QProcess proc;
0100             proc.setProcessChannelMode(QProcess::ForwardedChannels);
0101             proc.start(QStringLiteral("diff"), {QStringLiteral("-u"), icalFile, failFile.fileName()});
0102             QVERIFY(proc.waitForFinished());
0103         }
0104 
0105         QCOMPARE(newEvent->dtStart(), refEvent->dtStart());
0106         QCOMPARE(newEvent->dtEnd(), refEvent->dtEnd());
0107         QCOMPARE(newEvent->customProperty("KITINERARY", "RESERVATION"), refEvent->customProperty("KITINERARY", "RESERVATION"));
0108         QCOMPARE(newEvent->description(), refEvent->description());
0109         QVERIFY(*newEvent == *refEvent);
0110     }
0111 
0112     void testFindEvent_data()
0113     {
0114         QTest::addColumn<QString>("jsonFile");
0115         QTest::addColumn<QString>("icalFile");
0116 
0117         QDir dir(QStringLiteral(SOURCE_DIR "/calendarhandlerdata"));
0118         const auto lst = dir.entryList(QStringList(QStringLiteral("*.json")), QDir::Files | QDir::Readable | QDir::NoSymLinks);
0119         for (const auto &file : lst) {
0120             const QString refFile = dir.path() + QLatin1Char('/') + file.left(file.size() - 4) + QStringLiteral("ics");
0121             if (!QFile::exists(refFile)) {
0122                 qDebug() << "reference file" << refFile << "does not exist, skipping test file" << file;
0123                 continue;
0124             }
0125             QTest::newRow(file.toLatin1().constData()) << QString(dir.path() + QLatin1Char('/') +  file) << refFile;
0126         }
0127     }
0128 
0129     void testFindEvent()
0130     {
0131         QFETCH(QString, jsonFile);
0132         QFETCH(QString, icalFile);
0133 
0134         QFile f(jsonFile);
0135         QVERIFY(f.open(QFile::ReadOnly));
0136         const auto inArray = QJsonDocument::fromJson(f.readAll()).array();
0137         QVERIFY(!inArray.isEmpty());
0138         const auto preData = JsonLdDocument::fromJson(inArray);
0139         QCOMPARE(inArray.size(), preData.size());
0140 
0141         ExtractorPostprocessor postproc;
0142         postproc.process(preData);
0143         QCOMPARE(inArray.size(), postproc.result().size());
0144 
0145         MemoryCalendar::Ptr refCal(new MemoryCalendar(QTimeZone::systemTimeZone()));
0146         ICalFormat format;
0147         QVERIFY(format.load(refCal, icalFile));
0148 
0149         const auto events = CalendarHandler::findEvents(refCal, postproc.result().at(0));
0150         QCOMPARE(events.size(), 1);
0151         QVERIFY(events[0]);
0152     }
0153 
0154     void testFindEventForCancellation()
0155     {
0156         QFile f(QStringLiteral(SOURCE_DIR "/mergedata/cancellation.rhs.json"));
0157         QVERIFY(f.open(QFile::ReadOnly));
0158         const auto in = JsonLdDocument::fromJson(QJsonDocument::fromJson(f.readAll()).array());
0159         QCOMPARE(in.size(), 1);
0160         const auto cancel = in[0];
0161 
0162         MemoryCalendar::Ptr refCal(new MemoryCalendar(QTimeZone::systemTimeZone()));
0163         ICalFormat format;
0164         QVERIFY(format.load(refCal, QStringLiteral(SOURCE_DIR "/calendarhandlerdata/to-be-cancelled.ics")));
0165 
0166         const auto events = CalendarHandler::findEvents(refCal, cancel);
0167         QCOMPARE(events.size(), 2);
0168         QVERIFY(events[0]);
0169         QVERIFY(events[1]);
0170     }
0171 };
0172 
0173 QTEST_APPLESS_MAIN(CalendarHandlerTest)
0174 
0175 #include "calendarhandlertest.moc"