File indexing completed on 2024-11-24 04:44:10

0001 /*
0002  * SPDX-FileCopyrightText: 2012 Christian Mollekopf <mollekopf@kolabsys.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #ifndef TESTUTILS_H
0008 #define TESTUTILS_H
0009 
0010 #include <kolabevent.h>
0011 
0012 #include "kolabformat/kolabobject.h"
0013 
0014 #include <KMime/Message>
0015 
0016 #include <QFile>
0017 #include <QRegularExpression>
0018 #include <QUuid>
0019 
0020 Q_DECLARE_METATYPE(Kolab::ObjectType)
0021 Q_DECLARE_METATYPE(Kolab::Version)
0022 
0023 #define KCOMPARE(actual, expected)                                                                                                                             \
0024     do {                                                                                                                                                       \
0025         if (!(actual == expected)) {                                                                                                                           \
0026             qDebug() << __FILE__ << ':' << __LINE__ << "Actual: " #actual ": " << actual << "\nExpected: " #expected ": " << expected;                         \
0027             return false;                                                                                                                                      \
0028         }                                                                                                                                                      \
0029     } while (0)
0030 
0031 #endif
0032 
0033 #define DIFFCOMPARE(actual, expected)                                                                                                                          \
0034     do {                                                                                                                                                       \
0035         if (!(actual.simplified() == expected.simplified())) {                                                                                                 \
0036             qDebug() << "Content not the same.";                                                                                                               \
0037             qDebug() << "actual." << actual.simplified() << "\n";                                                                                              \
0038             qDebug() << "expected." << expected.simplified();                                                                                                  \
0039             showDiff(expected, actual);                                                                                                                        \
0040             QTest::qFail("Compared versions differ.", __FILE__, __LINE__);                                                                                     \
0041             return;                                                                                                                                            \
0042         }                                                                                                                                                      \
0043     } while (0)
0044 
0045 #define TESTVALUE(type, name) *static_cast<type *>(QTest::qData(#name, ::qMetaTypeId<type>()))
0046 
0047 const QString TESTFILEDIR = QString::fromLatin1(TEST_DATA_PATH "/testfiles/");
0048 
0049 QString getPath(const char *file)
0050 {
0051     return TESTFILEDIR + QString::fromLatin1(file);
0052 }
0053 
0054 void showDiff(const QString &expected, const QString &converted)
0055 {
0056     if (expected.isEmpty() || converted.isEmpty()) {
0057         qWarning() << "files are empty";
0058         return;
0059     }
0060     if (expected == converted) {
0061         qWarning() << "contents are the same";
0062         return;
0063     }
0064 
0065     qDebug() << "EXPECTED: " << expected;
0066     qDebug() << "CONVERTED: " << converted;
0067 }
0068 
0069 KMime::Message::Ptr readMimeFile(const QString &fileName, bool &ok)
0070 {
0071     //   qDebug() << fileName;
0072     QFile file(fileName);
0073     ok = file.open(QFile::ReadOnly);
0074     if (!ok) {
0075         qWarning() << "failed to open file: " << fileName;
0076         return {};
0077     }
0078     const QByteArray data = file.readAll();
0079 
0080     KMime::Message::Ptr msg = KMime::Message::Ptr(new KMime::Message);
0081     msg->setContent(data);
0082     msg->parse();
0083 
0084     return msg;
0085 }
0086 
0087 void normalizeMimemessage(QString &content)
0088 {
0089     content.replace(QRegularExpression(QStringLiteral("\\bLibkolab-\\d.\\d.\\d\\b")), QStringLiteral("Libkolab-x.x.x"));
0090     content.replace(QRegularExpression(QStringLiteral("\\bLibkolabxml-\\d.\\d.\\d\\b")), QStringLiteral("Libkolabxml-x.x.x"));
0091     content.replace(QRegularExpression(QStringLiteral("\\bLibkolab-\\d.\\d\\b")), QStringLiteral("Libkolab-x.x.x"));
0092     content.replace(QRegularExpression(QStringLiteral("\\bkdepim-runtime-\\d.\\d\\b")), QStringLiteral("Libkolab-x.x.x"));
0093     content.replace(QRegularExpression(QStringLiteral("\\bLibkolabxml-\\d.\\d\\b")), QStringLiteral("Libkolabxml-x.x.x"));
0094 
0095     content.replace(QRegularExpression(QStringLiteral("<uri>cid:.*@kolab.resource.akonadi</uri>")), QStringLiteral("<uri>cid:id@kolab.resource.akonadi</uri>"));
0096 
0097     content.replace(QRegularExpression(QStringLiteral("Content-ID: <.*@kolab.resource.akonadi>")), QStringLiteral("Content-ID: <id@kolab.resource.akonadi>"));
0098 
0099     content.replace(QRegularExpression(QStringLiteral("<uri>mailto:.*</uri>")), QStringLiteral("<uri>mailto:</uri>"));
0100     content.replace(QRegularExpression(QStringLiteral("<cal-address>mailto:.*</cal-address>")), QStringLiteral("<cal-address>mailto:</cal-address>"));
0101 
0102     content.replace(QRegularExpression(QStringLiteral("<uri>data:.*</uri>")), QStringLiteral("<uri>data:</uri>"));
0103 
0104     content.replace(QRegularExpression(QStringLiteral("<last-modification-date>.*</last-modification-date>")),
0105                     QStringLiteral("<last-modification-date></last-modification-date>"));
0106     // We no longer support pobox, so remove pobox lines
0107     content.remove(QRegularExpression(QStringLiteral("<pobox>.*</pobox>")));
0108 
0109     content.replace(QRegularExpression(QStringLiteral("<timestamp>.*</timestamp>")), QStringLiteral("<timestamp></timestamp>"));
0110 
0111     // DotMatchesEverythingOption because <x-koblab-version> spans multiple lines
0112     content.replace(QRegularExpression(QStringLiteral("<x-kolab-version>.*</x-kolab-version>"), QRegularExpression::DotMatchesEverythingOption),
0113                     QStringLiteral("<x-kolab-version></x-kolab-version>"));
0114 
0115     content.replace(QRegularExpression(QStringLiteral("--nextPart\\S*")), QStringLiteral("--part"));
0116     content.replace(QRegularExpression(QStringLiteral("\\bboundary=\"nextPart[^\\n]*")), QStringLiteral("boundary"));
0117 
0118     content.replace(QRegularExpression(QStringLiteral("Date[^\\n]*")), QStringLiteral("Date"));
0119     // The sort order of the attributes in kolabV2 is unpredictable
0120     content.replace(QRegularExpression(QStringLiteral("<x-custom.*/>")), QStringLiteral("<x-custom/>"));
0121     // quoted-printable encoding changes where the linebreaks are every now and then (an all are valid), so we remove the linebreaks
0122     content.remove(QLatin1StringView("=\n"));
0123 }
0124 
0125 QString normalizeVCardMessage(QString content)
0126 {
0127     // The encoding changes every now and then
0128     content.replace(QRegularExpression(QStringLiteral("ENCODING=b;TYPE=png:.*")), QStringLiteral("ENCODING=b;TYPE=png:picturedata"));
0129     return content;
0130 }
0131 
0132 // Normalize incidences for comparison
0133 void normalizeIncidence(KCalendarCore::Incidence::Ptr incidence)
0134 {
0135     // The UID is not persistent (it's just the internal pointer), therefore we clear it
0136     // TODO make sure that the UID does really not need to be persistent
0137     auto attendees = incidence->attendees();
0138     for (auto &attendee : attendees) {
0139         attendee.setUid(attendee.email());
0140     }
0141     incidence->setAttendees(attendees);
0142 
0143     // FIXME even if hasDueDate can differ, it shouldn't because it breaks equality. Check why they differ in the first place.
0144     if (incidence->type() == KCalendarCore::IncidenceBase::TypeTodo) {
0145         KCalendarCore::Todo::Ptr todo = incidence.dynamicCast<KCalendarCore::Todo>();
0146         Q_ASSERT(todo.data());
0147         if (!todo->hasDueDate() && !todo->hasStartDate()) {
0148             todo->setAllDay(false); // all day has no meaning if there are no start and due dates but may differ nevertheless
0149         }
0150     }
0151 }
0152 
0153 template<template<typename> class Op, typename T>
0154 static bool LexicographicalCompare(const T &_x, const T &_y)
0155 {
0156     T x(_x);
0157     x.setId(QString());
0158     T y(_y);
0159     y.setId(QString());
0160     Op<QString> op;
0161     return op(x.toString(), y.toString());
0162 }
0163 
0164 bool normalizePhoneNumbers(KContacts::Addressee &addressee, KContacts::Addressee &refAddressee)
0165 {
0166     KContacts::PhoneNumber::List phoneNumbers = addressee.phoneNumbers();
0167     KContacts::PhoneNumber::List refPhoneNumbers = refAddressee.phoneNumbers();
0168     if (phoneNumbers.size() != refPhoneNumbers.size()) {
0169         return false;
0170     }
0171     std::sort(phoneNumbers.begin(), phoneNumbers.end(), LexicographicalCompare<std::less, KContacts::PhoneNumber>);
0172     std::sort(refPhoneNumbers.begin(), refPhoneNumbers.end(), LexicographicalCompare<std::less, KContacts::PhoneNumber>);
0173 
0174     for (int i = 0; i < phoneNumbers.size(); ++i) {
0175         KContacts::PhoneNumber phoneNumber = phoneNumbers.at(i);
0176         const KContacts::PhoneNumber refPhoneNumber = refPhoneNumbers.at(i);
0177         KCOMPARE(LexicographicalCompare<std::equal_to>(phoneNumber, refPhoneNumber), true);
0178         addressee.removePhoneNumber(phoneNumber);
0179         phoneNumber.setId(refPhoneNumber.id());
0180         addressee.insertPhoneNumber(phoneNumber);
0181         // Make sure that both have the same sorted order
0182         refAddressee.removePhoneNumber(refPhoneNumber);
0183         refAddressee.insertPhoneNumber(refPhoneNumber);
0184     }
0185     //     for ( int i = 0; i < phoneNumbers.size(); ++i ) {
0186     //         qDebug() << "--------------------------------------";
0187     //         qDebug() << addressee.phoneNumbers().at(i).toString();
0188     //         qDebug() << refAddressee.phoneNumbers().at(i).toString();
0189     //     }
0190 
0191     return true;
0192 }
0193 
0194 bool normalizeAddresses(KContacts::Addressee &addressee, const KContacts::Addressee &refAddressee)
0195 {
0196     KContacts::Address::List addresses = addressee.addresses();
0197     KContacts::Address::List refAddresses = refAddressee.addresses();
0198     if (addresses.size() != refAddresses.size()) {
0199         return false;
0200     }
0201     std::sort(addresses.begin(), addresses.end(), LexicographicalCompare<std::less, KContacts::Address>);
0202     std::sort(refAddresses.begin(), refAddresses.end(), LexicographicalCompare<std::less, KContacts::Address>);
0203 
0204     for (int i = 0; i < addresses.size(); ++i) {
0205         KContacts::Address address = addresses.at(i);
0206         const KContacts::Address refAddress = refAddresses.at(i);
0207         KCOMPARE(LexicographicalCompare<std::equal_to>(address, refAddress), true);
0208         addressee.removeAddress(address);
0209         address.setId(refAddress.id());
0210         addressee.insertAddress(address);
0211     }
0212 
0213     return true;
0214 }
0215 
0216 void normalizeContact(KContacts::Addressee &addressee)
0217 {
0218     const KContacts::Address::List addresses = addressee.addresses();
0219 
0220     for (KContacts::Address a : addresses) {
0221         addressee.removeAddress(a);
0222         a.setPostOfficeBox(QString()); // Not supported anymore
0223         addressee.insertAddress(a);
0224     }
0225     addressee.setSound(KContacts::Sound()); // Sound is not supported
0226 
0227     addressee.removeCustom(QStringLiteral("KOLAB"), QStringLiteral("CreationDate")); // The creation date is no longer existing
0228 
0229     // Attachment names are no longer required because we identify the parts by cid and no longer by name
0230     addressee.removeCustom(QStringLiteral("KOLAB"), QStringLiteral("LogoAttachmentName"));
0231     addressee.removeCustom(QStringLiteral("KOLAB"), QStringLiteral("PictureAttachmentName"));
0232     addressee.removeCustom(QStringLiteral("KOLAB"), QStringLiteral("SoundAttachmentName"));
0233 }
0234 
0235 Kolab::Event createEvent(const Kolab::cDateTime &start, const Kolab::cDateTime &end)
0236 {
0237     Kolab::Event event;
0238     event.setUid(QUuid::createUuid().toString().toStdString());
0239     event.setStart(start);
0240     event.setEnd(end);
0241     return event;
0242 }