File indexing completed on 2024-04-21 03:54:47

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2004-2006 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2008 Norbert Frese <nf2@scheinwelt.at>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "jobremotetest.h"
0010 
0011 #include <QDebug>
0012 #include <QDir>
0013 #include <QEventLoop>
0014 #include <QStandardPaths>
0015 #include <QTest>
0016 #include <QUrl>
0017 
0018 #include <KLocalizedString>
0019 
0020 #include <kio/copyjob.h>
0021 #include <kio/deletejob.h>
0022 #include <kio/directorysizejob.h>
0023 #include <kio/filejob.h>
0024 #include <kio/mkdirjob.h>
0025 #include <kio/statjob.h>
0026 #include <kio/storedtransferjob.h>
0027 #include <kprotocolinfo.h>
0028 // #include "kiotesthelper.h" // createTestFile etc.
0029 
0030 QTEST_MAIN(JobRemoteTest)
0031 
0032 QDateTime s_referenceTimeStamp;
0033 
0034 // The code comes partly from jobtest.cpp
0035 
0036 static QUrl remoteTmpUrl()
0037 {
0038     QString customDir(qgetenv("KIO_JOBREMOTETEST_REMOTETMP"));
0039     if (customDir.isEmpty()) {
0040         return QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + '/');
0041     } else {
0042         // Could be a path or a URL
0043         return QUrl::fromUserInput(customDir + '/');
0044     }
0045 }
0046 
0047 static QString localTmpDir()
0048 {
0049 #ifdef Q_OS_WIN
0050     return QDir::tempPath() + "/jobremotetest/";
0051 #else
0052     // This one needs to be on another partition
0053     return QStringLiteral("/tmp/jobremotetest/");
0054 #endif
0055 }
0056 
0057 static bool myExists(const QUrl &url)
0058 {
0059     KIO::Job *job = KIO::stat(url, KIO::StatJob::DestinationSide, KIO::StatBasic, KIO::HideProgressInfo);
0060     job->setUiDelegate(nullptr);
0061     return job->exec();
0062 }
0063 
0064 static bool myMkdir(const QUrl &url)
0065 {
0066     KIO::Job *job = KIO::mkdir(url, -1);
0067     job->setUiDelegate(nullptr);
0068     return job->exec();
0069 }
0070 
0071 void JobRemoteTest::initTestCase()
0072 {
0073     QStandardPaths::setTestModeEnabled(true);
0074 
0075     s_referenceTimeStamp = QDateTime::currentDateTime().addSecs(-30); // 30 seconds ago
0076 
0077     // Start with a clean base dir
0078     cleanupTestCase();
0079     QUrl url = remoteTmpUrl();
0080     if (!myExists(url)) {
0081         const bool ok = url.isLocalFile() ? QDir().mkpath(url.toLocalFile()) : myMkdir(url);
0082         if (!ok) {
0083             qFatal("couldn't create %s", qPrintable(url.toString()));
0084         }
0085     }
0086     const bool ok = QDir().mkpath(localTmpDir());
0087     if (!ok) {
0088         qFatal("couldn't create %s", qPrintable(localTmpDir()));
0089     }
0090 }
0091 
0092 static void delDir(const QUrl &pathOrUrl)
0093 {
0094     KIO::Job *job = KIO::del(pathOrUrl, KIO::HideProgressInfo);
0095     job->setUiDelegate(nullptr);
0096     job->exec();
0097 }
0098 
0099 void JobRemoteTest::cleanupTestCase()
0100 {
0101     delDir(remoteTmpUrl());
0102     delDir(QUrl::fromLocalFile(localTmpDir()));
0103 }
0104 
0105 void JobRemoteTest::enterLoop()
0106 {
0107     QEventLoop eventLoop;
0108     connect(this, &JobRemoteTest::exitLoop, &eventLoop, &QEventLoop::quit);
0109     eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
0110 }
0111 
0112 /////
0113 
0114 void JobRemoteTest::putAndGet()
0115 {
0116     QUrl u(remoteTmpUrl());
0117     u.setPath(u.path() + "putAndGetFile");
0118     KIO::TransferJob *job = KIO::put(u, 0600, KIO::Overwrite | KIO::HideProgressInfo);
0119     quint64 secsSinceEpoch = QDateTime::currentSecsSinceEpoch(); // Use second granularity, supported on all filesystems
0120     QDateTime mtime = QDateTime::fromSecsSinceEpoch(secsSinceEpoch - 30); // 30 seconds ago
0121     job->setModificationTime(mtime);
0122     job->setUiDelegate(nullptr);
0123     connect(job, &KJob::result, this, &JobRemoteTest::slotResult);
0124     connect(job, &KIO::TransferJob::dataReq, this, &JobRemoteTest::slotDataReq);
0125     m_result = -1;
0126     m_dataReqCount = 0;
0127     enterLoop();
0128     QVERIFY(m_result == 0); // no error
0129 
0130     m_result = -1;
0131 
0132     KIO::StoredTransferJob *getJob = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
0133     getJob->setUiDelegate(nullptr);
0134     connect(getJob, &KJob::result, this, &JobRemoteTest::slotGetResult);
0135     enterLoop();
0136     QCOMPARE(m_result, 0); // no error
0137     QCOMPARE(m_data, QByteArray("This is a test for KIO::put()\n"));
0138     // QCOMPARE( m_data.size(), 11 );
0139 }
0140 
0141 void JobRemoteTest::slotGetResult(KJob *job)
0142 {
0143     m_result = job->error();
0144     m_data = static_cast<KIO::StoredTransferJob *>(job)->data();
0145     Q_EMIT exitLoop();
0146 }
0147 
0148 void JobRemoteTest::slotDataReq(KIO::Job *, QByteArray &data)
0149 {
0150     // Really not the way you'd write a slotDataReq usually :)
0151     switch (m_dataReqCount++) {
0152     case 0:
0153         data = "This is a test for ";
0154         break;
0155     case 1:
0156         data = "KIO::put()\n";
0157         break;
0158     case 2:
0159         data = QByteArray();
0160         break;
0161     }
0162 }
0163 
0164 void JobRemoteTest::slotResult(KJob *job)
0165 {
0166     m_result = job->error();
0167     Q_EMIT exitLoop();
0168 }
0169 
0170 ////
0171 
0172 void JobRemoteTest::openFileWriting()
0173 {
0174     m_rwCount = 0;
0175 
0176     QUrl u(remoteTmpUrl());
0177     u.setPath(u.path() + "openFileWriting");
0178     fileJob = KIO::open(u, QIODevice::WriteOnly);
0179 
0180     fileJob->setUiDelegate(nullptr);
0181     connect(fileJob, &KJob::result, this, &JobRemoteTest::slotResult);
0182     connect(fileJob, &KIO::FileJob::data, this, &JobRemoteTest::slotFileJobData);
0183     connect(fileJob, &KIO::FileJob::open, this, &JobRemoteTest::slotFileJobOpen);
0184     connect(fileJob, &KIO::FileJob::written, this, &JobRemoteTest::slotFileJobWritten);
0185     connect(fileJob, &KIO::FileJob::position, this, &JobRemoteTest::slotFileJobPosition);
0186     connect(fileJob, &KIO::FileJob::fileClosed, this, &JobRemoteTest::slotFileJobClose);
0187 
0188     m_result = -1;
0189     m_closeSignalCalled = false;
0190 
0191     enterLoop();
0192     QEXPECT_FAIL("", "Needs fixing in kio_file", Abort);
0193     QVERIFY(m_result == 0); // no error
0194 
0195     KIO::StoredTransferJob *getJob = KIO::storedGet(u, KIO::NoReload, KIO::HideProgressInfo);
0196     getJob->setUiDelegate(nullptr);
0197     connect(getJob, &KJob::result, this, &JobRemoteTest::slotGetResult);
0198     enterLoop();
0199     QCOMPARE(m_result, 0); // no error
0200     QVERIFY(m_closeSignalCalled); // close signal called.
0201     qDebug() << "m_data: " << m_data;
0202     QCOMPARE(m_data, QByteArray("test....test....test....test....test....test....end"));
0203 }
0204 
0205 void JobRemoteTest::slotFileJobData(KIO::Job *job, const QByteArray &data)
0206 {
0207     Q_UNUSED(job);
0208     Q_UNUSED(data);
0209 }
0210 
0211 void JobRemoteTest::slotFileJobRedirection(KIO::Job *job, const QUrl &url)
0212 {
0213     Q_UNUSED(job);
0214     Q_UNUSED(url);
0215 }
0216 
0217 void JobRemoteTest::slotFileJobMimetype(KIO::Job *job, const QString &type)
0218 {
0219     Q_UNUSED(job);
0220     Q_UNUSED(type);
0221 }
0222 
0223 void JobRemoteTest::slotFileJobOpen(KIO::Job *job)
0224 {
0225     Q_UNUSED(job);
0226     fileJob->seek(0);
0227 }
0228 
0229 void JobRemoteTest::slotFileJobWritten(KIO::Job *job, KIO::filesize_t written)
0230 {
0231     Q_UNUSED(job);
0232     Q_UNUSED(written);
0233     if (m_rwCount > 5) {
0234         fileJob->close();
0235     } else {
0236         fileJob->seek(m_rwCount * 8);
0237         m_rwCount++;
0238     }
0239 }
0240 
0241 void JobRemoteTest::slotFileJobPosition(KIO::Job *job, KIO::filesize_t offset)
0242 {
0243     Q_UNUSED(job);
0244     Q_UNUSED(offset);
0245     const QByteArray data("test....end");
0246     fileJob->write(data);
0247 }
0248 
0249 void JobRemoteTest::slotFileJobClose(KIO::Job *job)
0250 {
0251     Q_UNUSED(job);
0252     m_closeSignalCalled = true;
0253     qDebug() << "+++++++++ filejob closed";
0254 }
0255 
0256 ////
0257 
0258 void JobRemoteTest::openFileReading()
0259 {
0260     QUrl u(remoteTmpUrl());
0261     u.setPath(u.path() + "openFileReading");
0262 
0263     const QByteArray putData("test1test2test3test4test5");
0264 
0265     KIO::StoredTransferJob *putJob = KIO::storedPut(putData, u, 0600, KIO::Overwrite | KIO::HideProgressInfo);
0266 
0267     quint64 secsSinceEpoch = QDateTime::currentSecsSinceEpoch(); // Use second granularity, supported on all filesystems
0268     QDateTime mtime = QDateTime::fromSecsSinceEpoch(secsSinceEpoch - 30); // 30 seconds ago
0269     putJob->setModificationTime(mtime);
0270     putJob->setUiDelegate(nullptr);
0271     connect(putJob, &KJob::result, this, &JobRemoteTest::slotResult);
0272     m_result = -1;
0273     enterLoop();
0274     QVERIFY(m_result == 0); // no error
0275 
0276     m_rwCount = 4;
0277     m_data = QByteArray();
0278 
0279     fileJob = KIO::open(u, QIODevice::ReadOnly);
0280 
0281     fileJob->setUiDelegate(nullptr);
0282     connect(fileJob, &KJob::result, this, &JobRemoteTest::slotResult);
0283     connect(fileJob, &KIO::FileJob::data, this, &JobRemoteTest::slotFileJob2Data);
0284     connect(fileJob, &KIO::FileJob::open, this, &JobRemoteTest::slotFileJob2Open);
0285     connect(fileJob, &KIO::FileJob::written, this, &JobRemoteTest::slotFileJob2Written);
0286     connect(fileJob, &KIO::FileJob::position, this, &JobRemoteTest::slotFileJob2Position);
0287 
0288     // Can reuse this slot (same for all tests).
0289     connect(fileJob, &KIO::FileJob::fileClosed, this, &JobRemoteTest::slotFileJobClose);
0290 
0291     m_result = -1;
0292     m_closeSignalCalled = false;
0293 
0294     enterLoop();
0295     QVERIFY(m_result == 0); // no error
0296     QVERIFY(m_closeSignalCalled); // close signal called.
0297     qDebug() << "resulting m_data: " << QString(m_data);
0298     QCOMPARE(m_data, QByteArray("test5test4test3test2test1"));
0299 }
0300 
0301 void JobRemoteTest::slotFileJob2Data(KIO::Job *job, const QByteArray &data)
0302 {
0303     Q_UNUSED(job);
0304     qDebug() << "m_rwCount = " << m_rwCount << " data: " << data;
0305     m_data.append(data);
0306 
0307     if (m_rwCount < 0) {
0308         fileJob->close();
0309     } else {
0310         fileJob->seek(m_rwCount-- * 5);
0311     }
0312 }
0313 
0314 void JobRemoteTest::slotFileJob2Redirection(KIO::Job *job, const QUrl &url)
0315 {
0316     Q_UNUSED(job);
0317     Q_UNUSED(url);
0318 }
0319 
0320 void JobRemoteTest::slotFileJob2Mimetype(KIO::Job *job, const QString &type)
0321 {
0322     Q_UNUSED(job);
0323     qDebug() << "MIME type: " << type;
0324 }
0325 
0326 void JobRemoteTest::slotFileJob2Open(KIO::Job *job)
0327 {
0328     Q_UNUSED(job);
0329     fileJob->seek(m_rwCount-- * 5);
0330 }
0331 
0332 void JobRemoteTest::slotFileJob2Written(KIO::Job *job, KIO::filesize_t written)
0333 {
0334     Q_UNUSED(job);
0335     Q_UNUSED(written);
0336 }
0337 
0338 void JobRemoteTest::slotFileJob2Position(KIO::Job *job, KIO::filesize_t offset)
0339 {
0340     Q_UNUSED(job);
0341     qDebug() << "position : " << offset << " -> read (5)";
0342     fileJob->read(5);
0343 }
0344 
0345 ////
0346 
0347 void JobRemoteTest::slotMimetype(KIO::Job *job, const QString &type)
0348 {
0349     QVERIFY(job != nullptr);
0350     m_mimetype = type;
0351 }
0352 
0353 void JobRemoteTest::openFileRead0Bytes()
0354 {
0355     QUrl u(remoteTmpUrl());
0356     u.setPath(u.path() + "openFileReading");
0357 
0358     const QByteArray putData("Doesn't matter");
0359 
0360     KIO::StoredTransferJob *putJob = KIO::storedPut(putData, u, 0600, KIO::Overwrite | KIO::HideProgressInfo);
0361 
0362     quint64 secsSinceEpoch = QDateTime::currentSecsSinceEpoch(); // Use second granularity, supported on all filesystems
0363     QDateTime mtime = QDateTime::fromSecsSinceEpoch(secsSinceEpoch - 30); // 30 seconds ago
0364     putJob->setModificationTime(mtime);
0365     putJob->setUiDelegate(nullptr);
0366     connect(putJob, &KJob::result, this, &JobRemoteTest::slotResult);
0367     m_result = -1;
0368     enterLoop();
0369     QVERIFY(m_result == 0); // no error
0370 
0371     m_data = QByteArray();
0372 
0373     fileJob = KIO::open(u, QIODevice::ReadOnly);
0374 
0375     fileJob->setUiDelegate(nullptr);
0376     connect(fileJob, &KJob::result, this, &JobRemoteTest::slotResult);
0377     connect(fileJob, &KIO::FileJob::data, this, &JobRemoteTest::slotFileJob3Data);
0378     connect(fileJob, &KIO::FileJob::open, this, &JobRemoteTest::slotFileJob3Open);
0379     // Can reuse this slot (it's a noop).
0380     connect(fileJob, &KIO::FileJob::written, this, &JobRemoteTest::slotFileJob2Written);
0381     connect(fileJob, &KIO::FileJob::position, this, &JobRemoteTest::slotFileJob3Position);
0382 
0383     // Can reuse this as well.
0384     connect(fileJob, &KIO::FileJob::fileClosed, this, &JobRemoteTest::slotFileJobClose);
0385 
0386     m_result = -1;
0387     m_closeSignalCalled = false;
0388 
0389     enterLoop();
0390     // Previously reading 0 bytes would cause both data() and error() being emitted...
0391     QVERIFY(m_result == 0); // no error
0392     QVERIFY(m_closeSignalCalled); // close signal called.
0393 }
0394 
0395 void JobRemoteTest::slotFileJob3Open(KIO::Job *job)
0396 {
0397     Q_UNUSED(job);
0398     fileJob->seek(0);
0399 }
0400 
0401 void JobRemoteTest::slotFileJob3Position(KIO::Job *job, KIO::filesize_t offset)
0402 {
0403     Q_UNUSED(job);
0404     qDebug() << "position : " << offset << " -> read (0)";
0405     fileJob->read(0);
0406 }
0407 
0408 void JobRemoteTest::slotFileJob3Data(KIO::Job *job, const QByteArray &data)
0409 {
0410     Q_UNUSED(job);
0411     QVERIFY(data.isEmpty());
0412     fileJob->close();
0413 }
0414 
0415 void JobRemoteTest::openFileTruncating()
0416 {
0417     QUrl u(remoteTmpUrl());
0418     u.setPath(u.path() + "openFileTruncating");
0419 
0420     const QByteArray putData("test1");
0421 
0422     KIO::StoredTransferJob *putJob = KIO::storedPut(putData, u, 0600, KIO::Overwrite | KIO::HideProgressInfo);
0423 
0424     quint64 secsSinceEpoch = QDateTime::currentSecsSinceEpoch(); // Use second granularity, supported on all filesystems
0425     QDateTime mtime = QDateTime::fromSecsSinceEpoch(secsSinceEpoch - 30); // 30 seconds ago
0426     putJob->setModificationTime(mtime);
0427     putJob->setUiDelegate(nullptr);
0428     connect(putJob, &KJob::result, this, &JobRemoteTest::slotResult);
0429     m_result = -1;
0430     enterLoop();
0431     QVERIFY(m_result == 0); // no error
0432 
0433     m_truncatedFile.setFileName(u.toLocalFile());
0434     QVERIFY(m_truncatedFile.exists());
0435     QVERIFY(m_truncatedFile.open(QIODevice::ReadOnly));
0436     fileJob = KIO::open(u, QIODevice::ReadWrite);
0437 
0438     fileJob->setUiDelegate(nullptr);
0439     connect(fileJob, &KJob::result, this, &JobRemoteTest::slotResult);
0440     connect(fileJob, &KIO::FileJob::open, this, &JobRemoteTest::slotFileJob4Open);
0441     connect(fileJob, &KIO::FileJob::truncated, this, &JobRemoteTest::slotFileJob4Truncated);
0442 
0443     // Can reuse this slot (same for all tests).
0444     connect(fileJob, &KIO::FileJob::fileClosed, this, &JobRemoteTest::slotFileJobClose);
0445 
0446     m_result = -1;
0447     m_closeSignalCalled = false;
0448 
0449     enterLoop();
0450     QVERIFY(m_result == 0); // no error
0451     QVERIFY(m_closeSignalCalled); // close signal called.
0452 }
0453 
0454 void JobRemoteTest::slotFileJob4Open(KIO::Job *job)
0455 {
0456     Q_UNUSED(job);
0457     fileJob->truncate(10);
0458     qDebug() << "Truncating file to 10";
0459 }
0460 
0461 void JobRemoteTest::slotFileJob4Truncated(KIO::Job *job, KIO::filesize_t length)
0462 {
0463     Q_UNUSED(job);
0464     if (length == 10) {
0465         m_truncatedFile.seek(0);
0466         QCOMPARE(m_truncatedFile.readAll(), QByteArray("test1\x00\x00\x00\x00\x00", 10));
0467         fileJob->truncate(4);
0468         qDebug() << "Truncating file to 4";
0469     } else if (length == 4) {
0470         m_truncatedFile.seek(0);
0471         QCOMPARE(m_truncatedFile.readAll(), QByteArray("test"));
0472         fileJob->truncate(0);
0473         qDebug() << "Truncating file to 0";
0474     } else {
0475         m_truncatedFile.seek(0);
0476         QCOMPARE(m_truncatedFile.readAll(), QByteArray());
0477         fileJob->close();
0478         qDebug() << "Truncating file finished";
0479     }
0480 }
0481 
0482 #include "moc_jobremotetest.cpp"