File indexing completed on 2024-11-10 04:40:12

0001 /*
0002     SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "itemappendtest.h"
0008 #include "qtest_akonadi.h"
0009 
0010 #include "control.h"
0011 #include "testattribute.h"
0012 
0013 #include "agentinstance.h"
0014 #include "agentmanager.h"
0015 #include "attributefactory.h"
0016 #include "collectionfetchjob.h"
0017 #include "config_p.h"
0018 #include "itemcreatejob.h"
0019 #include "itemdeletejob.h"
0020 #include "itemfetchjob.h"
0021 #include "itemfetchscope.h"
0022 
0023 #include <QDebug>
0024 
0025 using namespace Akonadi;
0026 
0027 QTEST_AKONADIMAIN(ItemAppendTest)
0028 
0029 void ItemAppendTest::initTestCase()
0030 {
0031     // The size checks in the test expects Item payload to not be compressed
0032     QVERIFY(!Config::get().payloadCompression.enabled);
0033 
0034     AkonadiTest::checkTestIsIsolated();
0035     Control::start();
0036     AkonadiTest::setAllResourcesOffline();
0037     AttributeFactory::registerAttribute<TestAttribute>();
0038     AttributeFactory::registerAttribute<TestAttribute2>();
0039 }
0040 
0041 void ItemAppendTest::testItemAppend_data()
0042 {
0043     QTest::addColumn<QString>("remoteId");
0044 
0045     QTest::newRow("empty") << QString();
0046     QTest::newRow("non empty") << QStringLiteral("remote-id");
0047     QTest::newRow("whitespace") << QStringLiteral("remote id");
0048     QTest::newRow("quotes") << QStringLiteral("\"remote\" id");
0049     QTest::newRow("brackets") << QStringLiteral("[remote id]");
0050     QTest::newRow("RID length limit") << QStringLiteral("a").repeated(1024);
0051 }
0052 
0053 void ItemAppendTest::testItemAppend()
0054 {
0055     const Collection testFolder1(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0056     QVERIFY(testFolder1.isValid());
0057 
0058     QFETCH(QString, remoteId);
0059     Item ref; // for cleanup
0060 
0061     Item item(-1);
0062     item.setRemoteId(remoteId);
0063     item.setMimeType(QStringLiteral("application/octet-stream"));
0064     item.setFlag("TestFlag");
0065     item.setSize(3456);
0066     auto job = new ItemCreateJob(item, testFolder1, this);
0067     AKVERIFYEXEC(job);
0068     ref = job->item();
0069     QCOMPARE(ref.parentCollection(), testFolder1);
0070 
0071     auto fjob = new ItemFetchJob(testFolder1, this);
0072     fjob->fetchScope().setAncestorRetrieval(ItemFetchScope::Parent);
0073     AKVERIFYEXEC(fjob);
0074     QCOMPARE(fjob->items().count(), 1);
0075     QCOMPARE(fjob->items()[0], ref);
0076     QCOMPARE(fjob->items()[0].remoteId(), remoteId);
0077     QVERIFY(fjob->items()[0].flags().contains("TestFlag"));
0078     QCOMPARE(fjob->items()[0].parentCollection(), ref.parentCollection());
0079 
0080     qint64 size = 3456;
0081     QCOMPARE(fjob->items()[0].size(), size);
0082 
0083     auto djob = new ItemDeleteJob(ref, this);
0084     AKVERIFYEXEC(djob);
0085 
0086     fjob = new ItemFetchJob(testFolder1, this);
0087     AKVERIFYEXEC(fjob);
0088     QVERIFY(fjob->items().isEmpty());
0089 }
0090 
0091 void ItemAppendTest::testContent_data()
0092 {
0093     QTest::addColumn<QByteArray>("data");
0094 
0095     QTest::newRow("null") << QByteArray();
0096     QTest::newRow("empty") << QByteArray("");
0097     QTest::newRow("nullbyte") << QByteArray("\0", 1);
0098     QTest::newRow("nullbyte3") << QByteArray("\0X", 2);
0099     QString utf8string = QStringLiteral("äöüß@€µøđ¢©®");
0100     QTest::newRow("utf8") << utf8string.toUtf8();
0101     QTest::newRow("newlines") << QByteArray("\nsome\n\nbreaked\ncontent\n\n");
0102     QByteArray b;
0103     QTest::newRow("big") << b.fill('a', 1U << 20);
0104     QTest::newRow("bignull") << b.fill('\0', 1U << 20);
0105     QTest::newRow("bigcr") << b.fill('\r', 1U << 20);
0106     QTest::newRow("biglf") << b.fill('\n', 1U << 20);
0107 }
0108 
0109 void ItemAppendTest::testContent()
0110 {
0111     const Collection testFolder1(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0112     QVERIFY(testFolder1.isValid());
0113 
0114     QFETCH(QByteArray, data);
0115 
0116     Item item;
0117     item.setMimeType(QStringLiteral("application/octet-stream"));
0118     if (!data.isNull()) {
0119         item.setPayload(data);
0120     }
0121 
0122     auto job = new ItemCreateJob(item, testFolder1, this);
0123     AKVERIFYEXEC(job);
0124     Item ref = job->item();
0125 
0126     auto fjob = new ItemFetchJob(testFolder1, this);
0127     fjob->fetchScope().setCacheOnly(true);
0128     fjob->fetchScope().fetchFullPayload();
0129     AKVERIFYEXEC(fjob);
0130     QCOMPARE(fjob->items().count(), 1);
0131     Item item2 = fjob->items().first();
0132     QCOMPARE(item2.hasPayload(), !data.isNull());
0133     if (item2.hasPayload()) {
0134         QCOMPARE(item2.payload<QByteArray>(), data);
0135     }
0136 
0137     auto djob = new ItemDeleteJob(ref, this);
0138     AKVERIFYEXEC(djob);
0139 }
0140 
0141 void ItemAppendTest::testNewMimetype()
0142 {
0143     const Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0144     QVERIFY(col.isValid());
0145 
0146     Item item;
0147     item.setMimeType(QStringLiteral("application/new-type"));
0148     auto job = new ItemCreateJob(item, col, this);
0149     AKVERIFYEXEC(job);
0150 
0151     item = job->item();
0152     QVERIFY(item.isValid());
0153 
0154     auto fetch = new ItemFetchJob(item, this);
0155     AKVERIFYEXEC(fetch);
0156     QCOMPARE(fetch->items().count(), 1);
0157     QCOMPARE(fetch->items().first().mimeType(), item.mimeType());
0158 }
0159 
0160 void ItemAppendTest::testIllegalAppend()
0161 {
0162     const Collection testFolder1(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0163     QVERIFY(testFolder1.isValid());
0164 
0165     Item item;
0166     item.setMimeType(QStringLiteral("application/octet-stream"));
0167 
0168     // adding item to non-existing collection
0169     auto job = new ItemCreateJob(item, Collection(INT_MAX), this);
0170     QVERIFY(!job->exec());
0171 
0172     // adding item into a collection which can't handle items of this type
0173     const Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res1/foo/bla")));
0174     QVERIFY(col.isValid());
0175     job = new ItemCreateJob(item, col, this);
0176     QEXPECT_FAIL("", "Test not yet implemented in the server.", Continue);
0177     QVERIFY(!job->exec());
0178 }
0179 
0180 void ItemAppendTest::testMultipartAppend()
0181 {
0182     const Collection testFolder1(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0183     QVERIFY(testFolder1.isValid());
0184 
0185     Item item;
0186     item.setMimeType(QStringLiteral("application/octet-stream"));
0187     item.setPayload<QByteArray>("body data");
0188     item.attribute<TestAttribute>(Item::AddIfMissing)->data = "extra data";
0189     item.setFlag("TestFlag");
0190     auto job = new ItemCreateJob(item, testFolder1, this);
0191     AKVERIFYEXEC(job);
0192     Item ref = job->item();
0193 
0194     auto fjob = new ItemFetchJob(ref, this);
0195     fjob->fetchScope().fetchFullPayload();
0196     fjob->fetchScope().fetchAttribute<TestAttribute>();
0197     AKVERIFYEXEC(fjob);
0198     QCOMPARE(fjob->items().count(), 1);
0199     item = fjob->items().first();
0200     QCOMPARE(item.payload<QByteArray>(), QByteArray("body data"));
0201     QVERIFY(item.hasAttribute<TestAttribute>());
0202     QCOMPARE(item.attribute<TestAttribute>()->data, QByteArray("extra data"));
0203     QVERIFY(item.flags().contains("TestFlag"));
0204 
0205     auto djob = new ItemDeleteJob(ref, this);
0206     AKVERIFYEXEC(djob);
0207 }
0208 
0209 void ItemAppendTest::testInvalidMultipartAppend()
0210 {
0211     Item item;
0212     item.setMimeType(QStringLiteral("application/octet-stream"));
0213     item.setPayload<QByteArray>("body data");
0214     item.attribute<TestAttribute>(Item::AddIfMissing)->data = "extra data";
0215     item.attribute<TestAttribute2>(Item::AddIfMissing)->data = "more extra data";
0216     item.setFlag("TestFlag");
0217     auto job = new ItemCreateJob(item, Collection(-1), this);
0218     QVERIFY(!job->exec());
0219 
0220     Item item2;
0221     item2.setMimeType(QStringLiteral("application/octet-stream"));
0222     item2.setPayload<QByteArray>("more body data");
0223     // Note the inverted order - intentional, but shouldn't impact the test in any way.
0224     item2.attribute<TestAttribute2>(Item::AddIfMissing)->data = "absolute horde of extra data";
0225     item2.attribute<TestAttribute>(Item::AddIfMissing)->data = "even more extra data";
0226     item2.setFlag("TestFlag");
0227     auto job2 = new ItemCreateJob(item2, Collection(-1), this);
0228     QVERIFY(!job2->exec());
0229 }
0230 
0231 void ItemAppendTest::testItemSize_data()
0232 {
0233     QTest::addColumn<Akonadi::Item>("item");
0234     QTest::addColumn<qint64>("size");
0235 
0236     Item i(QStringLiteral("application/octet-stream"));
0237     i.setPayload(QByteArray("ABCD"));
0238 
0239     QTest::newRow("auto size") << i << 4LL;
0240     i.setSize(3);
0241     QTest::newRow("too small") << i << 4LL;
0242     i.setSize(100);
0243     QTest::newRow("too large") << i << 100LL;
0244 }
0245 
0246 void ItemAppendTest::testItemSize()
0247 {
0248     QFETCH(Akonadi::Item, item);
0249     QFETCH(qint64, size);
0250 
0251     const Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0252     QVERIFY(col.isValid());
0253 
0254     auto create = new ItemCreateJob(item, col, this);
0255     AKVERIFYEXEC(create);
0256     Item newItem = create->item();
0257 
0258     auto fetch = new ItemFetchJob(newItem, this);
0259     AKVERIFYEXEC(fetch);
0260     QCOMPARE(fetch->items().count(), 1);
0261 
0262     QCOMPARE(fetch->items().first().size(), size);
0263 }
0264 
0265 void ItemAppendTest::testItemMerge_data()
0266 {
0267     QTest::addColumn<Akonadi::Item>("item1");
0268     QTest::addColumn<Akonadi::Item>("item2");
0269     QTest::addColumn<Akonadi::Item>("mergedItem");
0270     QTest::addColumn<bool>("silent");
0271 
0272     {
0273         Item i1(QStringLiteral("application/octet-stream"));
0274         i1.setPayload(QByteArray("ABCD"));
0275         i1.setSize(56); // take compression into account
0276         i1.setRemoteId(QStringLiteral("XYZ"));
0277         i1.setGid(QStringLiteral("XYZ"));
0278         i1.setFlag("TestFlag1");
0279         i1.setRemoteRevision(QStringLiteral("5"));
0280 
0281         Item i2(QStringLiteral("application/octet-stream"));
0282         i2.setPayload(QByteArray("DEFGH"));
0283         i2.setSize(60); // the compression into account
0284         i2.setRemoteId(QStringLiteral("XYZ"));
0285         i2.setGid(QStringLiteral("XYZ"));
0286         i2.setFlag("TestFlag2");
0287         i2.setRemoteRevision(QStringLiteral("6"));
0288 
0289         Item mergedItem(i2);
0290         mergedItem.setFlag("TestFlag1");
0291 
0292         QTest::newRow("merge") << i1 << i2 << mergedItem << false;
0293         QTest::newRow("merge (silent)") << i1 << i2 << mergedItem << true;
0294     }
0295     {
0296         Item i1(QStringLiteral("application/octet-stream"));
0297         i1.setPayload(QByteArray("ABCD"));
0298         i1.setSize(56); // take compression into account
0299         i1.setRemoteId(QStringLiteral("RID2"));
0300         i1.setGid(QStringLiteral("GID2"));
0301         i1.setFlag("TestFlag1");
0302         i1.setRemoteRevision(QStringLiteral("5"));
0303 
0304         Item i2(QStringLiteral("application/octet-stream"));
0305         i2.setRemoteId(QStringLiteral("RID2"));
0306         i2.setGid(QStringLiteral("GID2"));
0307         i2.setFlags(Item::Flags() << "TestFlag2");
0308         i2.setRemoteRevision(QStringLiteral("6"));
0309 
0310         Item mergedItem(i1);
0311         mergedItem.setFlags(i2.flags());
0312         mergedItem.setRemoteRevision(i2.remoteRevision());
0313 
0314         QTest::newRow("overwrite flags, and don't remove existing payload") << i1 << i2 << mergedItem << false;
0315         QTest::newRow("overwrite flags, and don't remove existing payload (silent)") << i1 << i2 << mergedItem << true;
0316     }
0317 }
0318 
0319 void ItemAppendTest::testItemMerge()
0320 {
0321     QFETCH(Akonadi::Item, item1);
0322     QFETCH(Akonadi::Item, item2);
0323     QFETCH(Akonadi::Item, mergedItem);
0324     QFETCH(bool, silent);
0325 
0326     const Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0327     QVERIFY(col.isValid());
0328 
0329     auto create = new ItemCreateJob(item1, col, this);
0330     AKVERIFYEXEC(create);
0331     const Item createdItem = create->item();
0332 
0333     auto merge = new ItemCreateJob(item2, col, this);
0334     ItemCreateJob::MergeOptions options = ItemCreateJob::GID | ItemCreateJob::RID;
0335     if (silent) {
0336         options |= ItemCreateJob::Silent;
0337     }
0338     merge->setMerge(options);
0339     AKVERIFYEXEC(merge);
0340 
0341     QCOMPARE(merge->item().id(), createdItem.id());
0342     if (!silent) {
0343         QCOMPARE(merge->item().gid(), mergedItem.gid());
0344         QCOMPARE(merge->item().remoteId(), mergedItem.remoteId());
0345         QCOMPARE(merge->item().remoteRevision(), mergedItem.remoteRevision());
0346         QCOMPARE(merge->item().payloadData(), mergedItem.payloadData());
0347         QCOMPARE(merge->item().size(), mergedItem.size());
0348         qDebug() << merge->item().flags() << mergedItem.flags();
0349         QCOMPARE(merge->item().flags(), mergedItem.flags());
0350     }
0351 
0352     if (merge->item().id() != createdItem.id()) {
0353         auto del = new ItemDeleteJob(merge->item(), this);
0354         AKVERIFYEXEC(del);
0355     }
0356     auto del = new ItemDeleteJob(createdItem, this);
0357     AKVERIFYEXEC(del);
0358 }
0359 
0360 void ItemAppendTest::testForeignPayload()
0361 {
0362     const Collection col(AkonadiTest::collectionIdFromPath(QStringLiteral("res2/space folder")));
0363     QVERIFY(col.isValid());
0364 
0365     const QString filePath = QString::fromUtf8(qgetenv("TMPDIR")) + QStringLiteral("/foreignPayloadFile.mbox");
0366     QFile file(filePath);
0367     QVERIFY(file.open(QIODevice::WriteOnly));
0368     file.write("123456789");
0369     file.close();
0370 
0371     Item item(QStringLiteral("application/octet-stream"));
0372     item.setPayloadPath(filePath);
0373     item.setRemoteId(QStringLiteral("RID3"));
0374     item.setSize(9);
0375 
0376     auto create = new ItemCreateJob(item, col, this);
0377     AKVERIFYEXEC(create);
0378 
0379     auto ref = create->item();
0380 
0381     auto fetch = new ItemFetchJob(ref, this);
0382     fetch->fetchScope().fetchFullPayload(true);
0383     AKVERIFYEXEC(fetch);
0384     const auto items = fetch->items();
0385     QCOMPARE(items.size(), 1);
0386     item = items[0];
0387 
0388     QVERIFY(item.hasPayload<QByteArray>());
0389     QCOMPARE(item.payload<QByteArray>(), QByteArray("123456789"));
0390 
0391     auto del = new ItemDeleteJob(item, this);
0392     AKVERIFYEXEC(del);
0393 
0394     // Make sure Akonadi does not delete a foreign payload
0395     QVERIFY(file.exists());
0396     QVERIFY(file.remove());
0397 }
0398 
0399 #include "moc_itemappendtest.cpp"