File indexing completed on 2024-06-09 05:07:01

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-or-later
0005  *
0006  */
0007 
0008 #include <QObject>
0009 
0010 #include "aktest.h"
0011 #include "entities.h"
0012 #include "fakeakonadiserver.h"
0013 #include "fakeconnection.h"
0014 
0015 #include "private/externalpartstorage_p.h"
0016 #include "private/standarddirs_p.h"
0017 
0018 #include "storage/parthelper.h"
0019 #include "storage/partstreamer.h"
0020 
0021 #include <QDir>
0022 #include <QSettings>
0023 #include <QTest>
0024 
0025 #include "private/scope_p.h"
0026 
0027 using namespace Akonadi;
0028 using namespace Akonadi::Server;
0029 
0030 Q_DECLARE_METATYPE(Akonadi::Server::PimItem)
0031 Q_DECLARE_METATYPE(Akonadi::Server::Part::Storage)
0032 
0033 class PartStreamerTest : public QObject
0034 {
0035     Q_OBJECT
0036 
0037     FakeAkonadiServer mAkonadi;
0038 
0039 public:
0040     PartStreamerTest()
0041     {
0042         // Set a very small threshold for easier testing
0043         const QString serverConfigFile = StandardDirs::serverConfigFile(StandardDirs::ReadWrite);
0044         QSettings settings(serverConfigFile, QSettings::IniFormat);
0045         settings.setValue(QStringLiteral("General/SizeThreshold"), 5);
0046 
0047         mAkonadi.init();
0048     }
0049 
0050     Protocol::ModifyItemsCommandPtr createCommand(const PimItem &item)
0051     {
0052         auto cmd = Protocol::ModifyItemsCommandPtr::create(item.id());
0053         cmd->setParts({"PLD:DATA"});
0054         return cmd;
0055     }
0056 
0057 private Q_SLOTS:
0058     void testStreamer_data()
0059     {
0060         QTest::addColumn<TestScenario::List>("scenarios");
0061         QTest::addColumn<QByteArray>("expectedPartName");
0062         QTest::addColumn<QByteArray>("expectedPartData");
0063         QTest::addColumn<QByteArray>("expectedFileData");
0064         QTest::addColumn<qint64>("expectedPartSize");
0065         QTest::addColumn<bool>("expectedChanged");
0066         QTest::addColumn<Part::Storage>("storage");
0067         QTest::addColumn<PimItem>("pimItem");
0068 
0069         PimItem item;
0070         item.setCollectionId(Collection::retrieveByName(QStringLiteral("Col A")).id());
0071         item.setMimeType(MimeType::retrieveByName(QStringLiteral("application/octet-stream")));
0072         item.setSize(1); // this will not match reality during the test, but that does not matter, as
0073         // that's not the subject of this test
0074         QVERIFY(item.insert());
0075 
0076         qint64 partId = -1;
0077         Part::List parts = Part::retrieveAll();
0078         if (parts.isEmpty()) {
0079             partId = 0;
0080         } else {
0081             partId = parts.last().id() + 1;
0082         }
0083 
0084         // Order of these tests matters!
0085         {
0086             TestScenario::List scenarios;
0087             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0088                       << TestScenario::create(5,
0089                                               TestScenario::ServerCmd,
0090                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0091                       << TestScenario::create(5,
0092                                               TestScenario::ClientCmd,
0093                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 3)))
0094                       << TestScenario::create(5,
0095                                               TestScenario::ServerCmd,
0096                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data))
0097                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "123"))
0098                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 1))
0099                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0100 
0101             QTest::newRow("item 1, internal") << scenarios << QByteArray("PLD:DATA") << QByteArray("123") << QByteArray() << 3ll << true << Part::Internal
0102                                               << item;
0103         }
0104 
0105         {
0106             TestScenario::List scenarios;
0107             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0108                       << TestScenario::create(5,
0109                                               TestScenario::ServerCmd,
0110                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0111                       << TestScenario::create(5,
0112                                               TestScenario::ClientCmd,
0113                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9)))
0114                       << TestScenario::create(
0115                              5,
0116                              TestScenario::ServerCmd,
0117                              Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r0").arg(partId)))
0118                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA"))
0119                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 2))
0120                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0121 
0122             QTest::newRow("item 1, change to external")
0123                 << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r0") << QByteArray("123456789") << 9ll << true << Part::External << item;
0124         }
0125 
0126         {
0127             TestScenario::List scenarios;
0128             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0129                       << TestScenario::create(5,
0130                                               TestScenario::ServerCmd,
0131                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0132                       << TestScenario::create(5,
0133                                               TestScenario::ClientCmd,
0134                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9)))
0135                       << TestScenario::create(
0136                              5,
0137                              TestScenario::ServerCmd,
0138                              Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r1").arg(partId)))
0139                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA"))
0140                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 3))
0141                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0142 
0143             QTest::newRow("item 1, update external") << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r1") << QByteArray("987654321") << 9ll << true
0144                                                      << Part::External << item;
0145         }
0146 
0147         {
0148             TestScenario::List scenarios;
0149             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0150                       << TestScenario::create(5,
0151                                               TestScenario::ServerCmd,
0152                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0153                       << TestScenario::create(5,
0154                                               TestScenario::ClientCmd,
0155                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9)))
0156                       << TestScenario::create(
0157                              5,
0158                              TestScenario::ServerCmd,
0159                              Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r2").arg(partId)))
0160                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA"))
0161                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 4))
0162                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0163 
0164             QTest::newRow("item 1, external, no change")
0165                 << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r2") << QByteArray("987654321") << 9ll << false << Part::External << item;
0166         }
0167 
0168         {
0169             TestScenario::List scenarios;
0170             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0171                       << TestScenario::create(5,
0172                                               TestScenario::ServerCmd,
0173                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0174                       << TestScenario::create(5,
0175                                               TestScenario::ClientCmd,
0176                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 4)))
0177                       << TestScenario::create(5,
0178                                               TestScenario::ServerCmd,
0179                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data))
0180                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "1234"))
0181                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 5))
0182                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0183 
0184             QTest::newRow("item 1, change to internal")
0185                 << scenarios << QByteArray("PLD:DATA") << QByteArray("1234") << QByteArray() << 4ll << true << Part::Internal << item;
0186         }
0187 
0188         {
0189             TestScenario::List scenarios;
0190             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item))
0191                       << TestScenario::create(5,
0192                                               TestScenario::ServerCmd,
0193                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0194                       << TestScenario::create(5,
0195                                               TestScenario::ClientCmd,
0196                                               Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 4)))
0197                       << TestScenario::create(5,
0198                                               TestScenario::ServerCmd,
0199                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data))
0200                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "1234"))
0201                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 6))
0202                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0203 
0204             QTest::newRow("item 1, internal, no change")
0205                 << scenarios << QByteArray("PLD:DATA") << QByteArray("1234") << QByteArray() << 4ll << false << Part::Internal << item;
0206         }
0207 
0208         // Insert new item
0209         PimItem item2 = item;
0210         QVERIFY(item2.insert());
0211 
0212         const QString foreignPath = FakeAkonadiServer::basePath() + QStringLiteral("/tmp/foreignPayloadFile");
0213         {
0214             TestScenario::List scenarios;
0215             scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item2))
0216                       << TestScenario::create(5,
0217                                               TestScenario::ServerCmd,
0218                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData))
0219                       << TestScenario::create(
0220                              5,
0221                              TestScenario::ClientCmd,
0222                              Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 3, 0, Protocol::PartMetaData::Foreign)))
0223                       << TestScenario::create(5,
0224                                               TestScenario::ServerCmd,
0225                                               Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data))
0226                       << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", foreignPath.toUtf8()))
0227                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item2.id(), 1))
0228                       << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create());
0229 
0230             QTest::newRow("item 2, new foreign part") << scenarios << QByteArray("PLD:DATA") << foreignPath.toUtf8() << QByteArray("123") << 3ll << false
0231                                                       << Part::Foreign << item2;
0232         }
0233     }
0234 
0235     void testStreamer()
0236     {
0237         QFETCH(TestScenario::List, scenarios);
0238         QFETCH(QByteArray, expectedPartName);
0239         QFETCH(QByteArray, expectedPartData);
0240         QFETCH(QByteArray, expectedFileData);
0241         QFETCH(qint64, expectedPartSize);
0242         QFETCH(Part::Storage, storage);
0243         QFETCH(PimItem, pimItem);
0244 
0245         if (storage == Part::External) {
0246             // Create the payload file now, since don't have means to react
0247             // directly to the streaming command
0248             QFile file(ExternalPartStorage::resolveAbsolutePath(expectedPartData));
0249             file.open(QIODevice::WriteOnly);
0250             file.write(expectedFileData);
0251             file.close();
0252         } else if (storage == Part::Foreign) {
0253             // Create the foreign payload file
0254             QDir().mkpath(FakeAkonadiServer::basePath() + QStringLiteral("/tmp"));
0255             QFile file(QString::fromUtf8(expectedPartData));
0256             file.open(QIODevice::WriteOnly);
0257             file.write(expectedFileData);
0258             file.close();
0259         }
0260 
0261         mAkonadi.setScenarios(scenarios);
0262         mAkonadi.runTest();
0263 
0264         PimItem item = PimItem::retrieveById(pimItem.id());
0265         const QList<Part> parts = item.parts();
0266         QVERIFY(parts.count() == 1);
0267         const Part part = parts[0];
0268         QCOMPARE(part.datasize(), expectedPartSize);
0269         QCOMPARE(part.storage(), storage);
0270         const QByteArray data = part.data();
0271 
0272         if (storage == Part::External) {
0273             QCOMPARE(data, expectedPartData);
0274             QFile file(ExternalPartStorage::resolveAbsolutePath(data));
0275             QVERIFY(file.exists());
0276             QCOMPARE(file.size(), expectedPartSize);
0277             QVERIFY(file.open(QIODevice::ReadOnly));
0278 
0279             const QByteArray fileData = file.readAll();
0280             QCOMPARE(fileData, expectedFileData);
0281 
0282             // Make sure no previous versions are left behind in file_db_data
0283             const int revision = data.mid(data.indexOf("_r") + 2).toInt();
0284             for (int i = 0; i < revision; ++i) {
0285                 const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(i);
0286                 const QString filePath = ExternalPartStorage::resolveAbsolutePath(fileName);
0287                 // TRY because the deletion happens in another thread
0288                 QTRY_VERIFY2(!QFile::exists(filePath), qPrintable(filePath));
0289             }
0290         } else if (storage == Part::Foreign) {
0291             QCOMPARE(data, expectedPartData);
0292             QFile file(QString::fromUtf8(data));
0293             QVERIFY(file.exists());
0294             QCOMPARE(file.size(), expectedPartSize);
0295             QVERIFY(file.open(QIODevice::ReadOnly));
0296 
0297             const QByteArray fileData = file.readAll();
0298             QCOMPARE(fileData, expectedFileData);
0299         } else {
0300             QCOMPARE(data, expectedPartData);
0301 
0302             // Make sure nothing is left behind in file_db_data
0303             // TODO: we have no way of knowing what is the last revision
0304             for (int i = 0; i <= 100; ++i) {
0305                 const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(i);
0306                 const QString filePath = ExternalPartStorage::resolveAbsolutePath(fileName);
0307                 QTRY_VERIFY2(!QFile::exists(filePath), qPrintable(filePath));
0308             }
0309         }
0310     }
0311 };
0312 
0313 AKTEST_FAKESERVER_MAIN(PartStreamerTest)
0314 
0315 #include "partstreamertest.moc"