File indexing completed on 2024-12-22 04:57:32

0001 /*
0002   This file is part of the kpimutils library.
0003 
0004   SPDX-FileCopyrightText: 2007 Till Adam <adam@kde.org>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "testmaildir.h"
0010 
0011 #include <memory>
0012 
0013 #include <Akonadi/MessageFlags>
0014 
0015 #include <QDir>
0016 #include <QFile>
0017 #include <QTemporaryDir>
0018 #include <QTest>
0019 
0020 QTEST_MAIN(MaildirTest)
0021 
0022 #include "../maildir.h"
0023 using namespace KPIM;
0024 
0025 static const char *testDir = "libmaildir-unit-test";
0026 static const char *testString = "From: theDukeOfMonmouth@uk.gov\n \nTo: theDukeOfBuccleuch@uk.gov\n\ntest\n";
0027 static const char *testStringHeaders = "From: theDukeOfMonmouth@uk.gov\n \nTo: theDukeOfBuccleuch@uk.gov\n";
0028 
0029 void MaildirTest::init()
0030 {
0031     QString tmpPath(QDir::tempPath() + QLatin1Char('/') + QLatin1StringView(testDir));
0032     QDir().mkpath(tmpPath);
0033     m_temp = new QTemporaryDir(tmpPath);
0034 
0035     QDir temp(m_temp->path());
0036     QVERIFY(temp.exists());
0037 
0038     temp.mkdir(QStringLiteral("new"));
0039     QVERIFY(temp.exists(QLatin1StringView("new")));
0040     temp.mkdir(QStringLiteral("cur"));
0041     QVERIFY(temp.exists(QLatin1StringView("cur")));
0042     temp.mkdir(QStringLiteral("tmp"));
0043     QVERIFY(temp.exists(QLatin1StringView("tmp")));
0044 }
0045 
0046 void MaildirTest::cleanup()
0047 {
0048     m_temp->remove();
0049     QDir d(m_temp->path());
0050     const QString subFolderPath(QStringLiteral(".%1.directory").arg(d.dirName()));
0051     QDir((subFolderPath)).removeRecursively();
0052 
0053     delete m_temp;
0054     m_temp = nullptr;
0055 }
0056 
0057 void MaildirTest::fillDirectory(const QString &name, int limit)
0058 {
0059     QFile file;
0060     QDir::setCurrent(m_temp->path() + QLatin1Char('/') + name);
0061     for (int i = 0; i < limit; i++) {
0062         file.setFileName(QLatin1StringView("testmail-") + QString::number(i));
0063         file.open(QIODevice::WriteOnly);
0064         file.write(testString);
0065         file.flush();
0066         file.close();
0067     }
0068 }
0069 
0070 void MaildirTest::createSubFolders()
0071 {
0072     QDir d(m_temp->path());
0073     const QString subFolderPath(QStringLiteral(".%1.directory").arg(d.dirName()));
0074     d.cdUp();
0075     d.mkdir(subFolderPath);
0076     d.cd(subFolderPath);
0077     d.mkdir(QStringLiteral("foo"));
0078     d.mkdir(QStringLiteral("barbar"));
0079     d.mkdir(QStringLiteral("bazbaz"));
0080 }
0081 
0082 void MaildirTest::fillNewDirectory()
0083 {
0084     fillDirectory(QStringLiteral("new"), 140);
0085 }
0086 
0087 void MaildirTest::fillCurrentDirectory()
0088 {
0089     fillDirectory(QStringLiteral("cur"), 20);
0090 }
0091 
0092 void MaildirTest::testMaildirInstantiation()
0093 {
0094     Maildir d(QStringLiteral("/foo/bar/Mail"));
0095     Maildir d2(d);
0096     Maildir d3;
0097     d3 = d;
0098     QVERIFY(d == d2);
0099     QVERIFY(d3 == d2);
0100     QVERIFY(d == d3);
0101     QCOMPARE(d.path(), QString(QLatin1StringView("/foo/bar/Mail")));
0102     QCOMPARE(d.name(), QString(QLatin1StringView("Mail")));
0103 
0104     QVERIFY(!d.isValid(false));
0105 
0106     Maildir good(m_temp->path());
0107     QVERIFY(good.isValid(false));
0108 
0109     QDir temp(m_temp->path());
0110     temp.rmdir(QStringLiteral("new"));
0111     QVERIFY(!good.isValid(false));
0112     QVERIFY(!good.lastError().isEmpty());
0113 
0114     Maildir root1(QStringLiteral("/foo/bar/Mail"), true);
0115     QVERIFY(root1.isRoot());
0116 
0117     Maildir root1Copy = root1;
0118     QCOMPARE(root1Copy.path(), root1.path());
0119     QCOMPARE(root1Copy.isRoot(), root1.isRoot());
0120 
0121     // FIXME test insufficient permissions?
0122 }
0123 
0124 void MaildirTest::testMaildirListing()
0125 {
0126     fillNewDirectory();
0127 
0128     Maildir d(m_temp->path());
0129     QStringList entries = d.entryList();
0130 
0131     QCOMPARE(entries.count(), 140);
0132 
0133     fillCurrentDirectory();
0134     entries = d.entryList();
0135     QCOMPARE(entries.count(), 160);
0136 }
0137 
0138 void MaildirTest::testMaildirAccess()
0139 {
0140     fillCurrentDirectory();
0141     Maildir d(m_temp->path());
0142     QStringList entries = d.entryList();
0143     QCOMPARE(entries.count(), 20);
0144 
0145     QByteArray data = d.readEntry(entries[0]);
0146     QVERIFY(!data.isEmpty());
0147     QCOMPARE(data, QByteArray(testString));
0148 }
0149 
0150 void MaildirTest::testMaildirReadHeaders()
0151 {
0152     fillCurrentDirectory();
0153     Maildir d(m_temp->path());
0154     QStringList entries = d.entryList();
0155     QCOMPARE(entries.count(), 20);
0156 
0157     QByteArray data = d.readEntryHeaders(entries[0]);
0158     QVERIFY(!data.isEmpty());
0159     QCOMPARE(data, QByteArray(testStringHeaders));
0160 }
0161 
0162 void MaildirTest::testMaildirWrite()
0163 {
0164     fillCurrentDirectory();
0165     Maildir d(m_temp->path());
0166     QStringList entries = d.entryList();
0167     QCOMPARE(entries.count(), 20);
0168 
0169     QByteArray data = d.readEntry(entries[0]);
0170     QByteArray data2 = "changed\n";
0171     QVERIFY(d.writeEntry(entries[0], data2));
0172     QCOMPARE(data2, d.readEntry(entries[0]));
0173 }
0174 
0175 void MaildirTest::testMaildirAppend()
0176 {
0177     Maildir d(m_temp->path());
0178     QByteArray data = "newentry\n";
0179     QString key = d.addEntry(data);
0180     QVERIFY(!key.isEmpty());
0181     QCOMPARE(data, d.readEntry(key));
0182 }
0183 
0184 void MaildirTest::testMaildirCreation()
0185 {
0186     QString p(QStringLiteral("CREATETEST"));
0187     QString tmpPath(QDir::tempPath() + QLatin1Char('/') + p);
0188     QDir().mkpath(tmpPath);
0189     std::unique_ptr<QTemporaryDir> temp(new QTemporaryDir(tmpPath));
0190     Maildir d(temp->path() + p);
0191     QVERIFY(!d.isValid(false));
0192     d.create();
0193     QVERIFY(d.isValid(false));
0194 }
0195 
0196 void MaildirTest::testMaildirRemoveEntry()
0197 {
0198     Maildir d(m_temp->path());
0199     QByteArray data = "newentry\n";
0200     QString key = d.addEntry(data);
0201     QVERIFY(!key.isEmpty());
0202     QCOMPARE(data, d.readEntry(key));
0203     QVERIFY(d.removeEntry(key));
0204     QVERIFY(d.readEntry(key).isEmpty());
0205 }
0206 
0207 void MaildirTest::testMaildirListSubfolders()
0208 {
0209     fillNewDirectory();
0210 
0211     Maildir d(m_temp->path());
0212     QStringList entries = d.subFolderList();
0213 
0214     QVERIFY(entries.isEmpty());
0215 
0216     createSubFolders();
0217 
0218     entries = d.subFolderList();
0219     QVERIFY(!entries.isEmpty());
0220     QCOMPARE(entries.count(), 3);
0221 }
0222 
0223 void MaildirTest::testMaildirCreateSubfolder()
0224 {
0225     Maildir d(m_temp->path());
0226     QStringList entries = d.subFolderList();
0227     QVERIFY(entries.isEmpty());
0228 
0229     d.addSubFolder(QStringLiteral("subFolderTest"));
0230     entries = d.subFolderList();
0231     QVERIFY(!entries.isEmpty());
0232     QCOMPARE(entries.count(), 1);
0233     Maildir child = d.subFolder(entries.first());
0234     QVERIFY(child.isValid(false));
0235 }
0236 
0237 void MaildirTest::testMaildirRemoveSubfolder()
0238 {
0239     Maildir d(m_temp->path());
0240     QVERIFY(d.isValid(false));
0241 
0242     QString folderPath = d.addSubFolder(QStringLiteral("subFolderTest"));
0243     QVERIFY(!folderPath.isEmpty());
0244     QVERIFY(folderPath.endsWith(QLatin1StringView(".directory/subFolderTest")));
0245     bool removingWorked = d.removeSubFolder(QStringLiteral("subFolderTest"));
0246     QVERIFY(removingWorked);
0247 }
0248 
0249 void MaildirTest::testMaildirRename()
0250 {
0251     Maildir d(m_temp->path());
0252     QVERIFY(d.isValid(false));
0253 
0254     QString folderPath = d.addSubFolder(QStringLiteral("rename me!"));
0255     QVERIFY(!folderPath.isEmpty());
0256 
0257     Maildir d2(folderPath);
0258     QVERIFY(d2.isValid(false));
0259     QVERIFY(d2.rename(QLatin1StringView("renamed")));
0260     QCOMPARE(d2.name(), QString(QLatin1StringView("renamed")));
0261 
0262     // same again, should not fail
0263     QVERIFY(d2.rename(QLatin1StringView("renamed")));
0264     QCOMPARE(d2.name(), QString(QLatin1StringView("renamed")));
0265 
0266     // already existing name
0267     QVERIFY(!d.addSubFolder(QLatin1StringView("this name is already taken")).isEmpty());
0268     QVERIFY(!d2.rename(QLatin1StringView("this name is already taken")));
0269 }
0270 
0271 void MaildirTest::testMaildirMoveTo()
0272 {
0273     Maildir d(m_temp->path());
0274     QVERIFY(d.isValid(false));
0275 
0276     QString folderPath1 = d.addSubFolder(QStringLiteral("child1"));
0277     QVERIFY(!folderPath1.isEmpty());
0278 
0279     Maildir d2(folderPath1);
0280     QVERIFY(d2.isValid(false));
0281 
0282     QDir d2Dir(d2.path());
0283     QVERIFY(d2Dir.exists());
0284 
0285     QString folderPath11 = d2.addSubFolder(QStringLiteral("grandchild1"));
0286 
0287     Maildir d21(folderPath11);
0288     QVERIFY(d21.isValid(false));
0289 
0290     QDir d2SubDir(Maildir::subDirPathForFolderPath(d2.path()));
0291     QVERIFY(d2SubDir.exists());
0292 
0293     QString folderPath2 = d.addSubFolder(QStringLiteral("child2"));
0294     QVERIFY(!folderPath2.isEmpty());
0295 
0296     Maildir d3(folderPath2);
0297     QVERIFY(d3.isValid(false));
0298 
0299     // move child1 to child2
0300     QVERIFY(d2.moveTo(d3));
0301 
0302     Maildir d31 = d3.subFolder(QStringLiteral("child1"));
0303     QVERIFY(d31.isValid(false));
0304 
0305     QVERIFY(!d2Dir.exists());
0306     QVERIFY(!d2SubDir.exists());
0307 
0308     QDir d31Dir(d31.path());
0309     QVERIFY(d31Dir.exists());
0310 
0311     QDir d31SubDir(Maildir::subDirPathForFolderPath(d31.path()));
0312     QVERIFY(d31SubDir.exists());
0313 
0314     Maildir d311 = d31.subFolder(QStringLiteral("grandchild1"));
0315     QVERIFY(d311.isValid(false));
0316 
0317     // try moving again
0318     d2 = Maildir(folderPath1);
0319     QVERIFY(!d2.isValid(false));
0320     QVERIFY(!d2.moveTo(d3));
0321 }
0322 
0323 void MaildirTest::testMaildirFlagsReading()
0324 {
0325     QFile file;
0326     const QStringList markers = QStringList() << QStringLiteral("P") << QStringLiteral("R") << QStringLiteral("S") << QStringLiteral("F")
0327                                               << QStringLiteral("FPRS");
0328     QDir::setCurrent(m_temp->path() + QStringLiteral("/cur"));
0329     for (int i = 0; i < 6; i++) {
0330         QString fileName = QLatin1StringView("testmail-") + QString::number(i);
0331         if (i < 5) {
0332             fileName +=
0333 #ifdef Q_OS_WIN
0334                 QLatin1StringView("!2,")
0335 #else
0336                 QLatin1StringView(":2,")
0337 #endif
0338                 + markers[i];
0339         }
0340         file.setFileName(fileName);
0341         file.open(QIODevice::WriteOnly);
0342         file.write(testString);
0343         file.flush();
0344         file.close();
0345     }
0346 
0347     Maildir d(m_temp->path());
0348     QStringList entries = d.entryList();
0349     // Maildir::entryList() doesn't sort for performance reasons,
0350     // do it here to make test sequence reliable.
0351     entries.sort();
0352 
0353     QCOMPARE(entries.count(), 6);
0354 
0355     Akonadi::Item::Flags flags = d.readEntryFlags(entries[0]);
0356     QCOMPARE(flags.count(), 1);
0357     QVERIFY(flags.contains(Akonadi::MessageFlags::Forwarded));
0358 
0359     flags = d.readEntryFlags(entries[1]);
0360     QCOMPARE(flags.count(), 1);
0361     QVERIFY(flags.contains(Akonadi::MessageFlags::Replied));
0362 
0363     flags = d.readEntryFlags(entries[2]);
0364     QCOMPARE(flags.count(), 1);
0365     QVERIFY(flags.contains(Akonadi::MessageFlags::Seen));
0366 
0367     flags = d.readEntryFlags(entries[3]);
0368     QCOMPARE(flags.count(), 1);
0369     QVERIFY(flags.contains(Akonadi::MessageFlags::Flagged));
0370 
0371     flags = d.readEntryFlags(entries[4]);
0372     QCOMPARE(flags.count(), 4);
0373     QVERIFY(flags.contains(Akonadi::MessageFlags::Forwarded));
0374     QVERIFY(flags.contains(Akonadi::MessageFlags::Replied));
0375     QVERIFY(flags.contains(Akonadi::MessageFlags::Seen));
0376     QVERIFY(flags.contains(Akonadi::MessageFlags::Flagged));
0377 
0378     flags = d.readEntryFlags(entries[5]);
0379     QVERIFY(flags.isEmpty());
0380 }
0381 
0382 void MaildirTest::testMaildirFlagsWriting_data()
0383 {
0384     QTest::addColumn<QString>("origDir");
0385     QTest::addColumn<QString>("origFileName");
0386     QTest::newRow("cur/") << "cur"
0387                           << "testmail";
0388     QTest::newRow("cur/S") << "cur"
0389                            << "testmail:2,S"; // wrongly marked as "seen" on disk (#289428)
0390     QTest::newRow("new/") << "new"
0391                           << "testmail";
0392     QTest::newRow("new/S") << "new"
0393                            << "testmail:2,S";
0394 }
0395 
0396 void MaildirTest::testMaildirFlagsWriting()
0397 {
0398     QFETCH(QString, origDir);
0399     QFETCH(QString, origFileName);
0400 
0401     // create an initially new mail
0402     QFile file;
0403     QDir::setCurrent(m_temp->path());
0404     file.setFileName(origDir + QLatin1Char('/') + origFileName);
0405     file.open(QIODevice::WriteOnly);
0406     file.write(testString);
0407     file.flush();
0408     file.close();
0409 
0410     // add a single flag
0411     Maildir d(m_temp->path());
0412     const QStringList entries = d.entryList();
0413     QCOMPARE(entries.size(), 1);
0414     QVERIFY(QFile::exists(origDir + QLatin1Char('/') + entries[0]));
0415     const QString newKey = d.changeEntryFlags(entries[0], Akonadi::Item::Flags() << Akonadi::MessageFlags::Seen);
0416     // make sure the new key exists
0417     QCOMPARE(newKey, d.entryList()[0]);
0418     QVERIFY(QFile::exists(QStringLiteral("cur/") + newKey));
0419     // and it's the right file
0420     QCOMPARE(d.readEntry(newKey), QByteArray(testString));
0421     // now check the file name
0422     QVERIFY(newKey.endsWith(QLatin1StringView("2,S")));
0423     // and more flags
0424     const QString newKey2 = d.changeEntryFlags(newKey, Akonadi::Item::Flags() << Akonadi::MessageFlags::Seen << Akonadi::MessageFlags::Replied);
0425     // check the file name, and the sorting of markers
0426     QVERIFY(newKey2.endsWith(QLatin1StringView("2,RS")));
0427     QVERIFY(QFile::exists(QStringLiteral("cur/") + newKey2));
0428 }
0429 
0430 #include "moc_testmaildir.cpp"