File indexing completed on 2024-03-24 15:33:40

0001 /*
0002     SPDX-FileCopyrightText: 2019 Ben Gruber <bengruber250@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 // This test suite is based on those in ftptest.cpp and uses the same test files.
0008 
0009 #include <kio/copyjob.h>
0010 #include <kio/storedtransferjob.h>
0011 
0012 #include <QBuffer>
0013 #include <QProcess>
0014 #include <QStandardPaths>
0015 #include <QTest>
0016 
0017 class WebDAVTest : public QObject
0018 {
0019     Q_OBJECT
0020 public:
0021     QUrl url(const QString &path) const
0022     {
0023         Q_ASSERT(path.startsWith(QChar('/')));
0024         QUrl newUrl = m_url;
0025         newUrl.setPath(path);
0026         newUrl.setPort(port);
0027         return newUrl;
0028     }
0029 
0030     QTemporaryDir m_remoteDir;
0031     QProcess m_daemonProc;
0032     QUrl m_url = QUrl("webdav://localhost");
0033     static const int port = 30000;
0034 
0035 private:
0036     static void runDaemon(QProcess &proc, const QTemporaryDir &remoteDir)
0037     {
0038         QVERIFY(remoteDir.isValid());
0039         const QString exec = QStandardPaths::findExecutable("wsgidav");
0040         if (exec.isEmpty()) {
0041             QFAIL("Could not find 'wsgidav' executable in PATH");
0042         }
0043         proc.setProgram(exec);
0044         proc.setArguments(
0045             {QStringLiteral("--host=0.0.0.0"), QString("--port=%1").arg(port), QString("--root=%1").arg(remoteDir.path()), QStringLiteral("--auth=anonymous")});
0046         proc.setProcessChannelMode(QProcess::ForwardedErrorChannel);
0047         proc.start();
0048         QVERIFY(proc.waitForStarted());
0049         QCOMPARE(proc.state(), QProcess::Running);
0050         // Wait for the daemon to print its port. That tells us both where it's listening
0051         // and also that it is ready to move ahead with testing.
0052         QVERIFY(QTest::qWaitFor(
0053             [&]() -> bool {
0054                 const QString out = proc.readAllStandardOutput();
0055                 if (!out.isEmpty()) {
0056                     qDebug() << "STDERR:" << out;
0057                 }
0058                 if (!out.endsWith("Serving on http://0.0.0.0:30000 ...\n")) {
0059                     return false;
0060                 }
0061                 return true;
0062             },
0063             5000));
0064     }
0065 
0066 private Q_SLOTS:
0067     void initTestCase()
0068     {
0069         // Force the http/webdav worker from our bindir as first choice. This specifically
0070         // works around the fact that the kioslave executable would load the worker from the system
0071         // as first choice instead of the one from the build dir.
0072         qputenv("QT_PLUGIN_PATH", QCoreApplication::applicationDirPath().toUtf8());
0073 
0074         // Start the webdav server.
0075         runDaemon(m_daemonProc, m_remoteDir);
0076         // Put a prefix on the stderr and stdout from the server.
0077         connect(&m_daemonProc, &QProcess::readyReadStandardError, this, [this] {
0078             qDebug() << "wsgidav STDERR:" << m_daemonProc.readAllStandardError();
0079         });
0080         connect(&m_daemonProc, &QProcess::readyReadStandardOutput, this, [this] {
0081             qDebug() << "wsgidav STDOUT:" << m_daemonProc.readAllStandardOutput();
0082         });
0083 
0084         QStandardPaths::setTestModeEnabled(true);
0085     }
0086 
0087     void cleanupTestCase()
0088     {
0089         m_daemonProc.terminate();
0090         m_daemonProc.kill();
0091         m_daemonProc.waitForFinished();
0092     }
0093 
0094     void init()
0095     {
0096         QCOMPARE(m_daemonProc.state(), QProcess::Running);
0097     }
0098 
0099     void testGet()
0100     {
0101         const QString path("/testGet");
0102         const auto url = this->url(path);
0103         const QString remotePath = m_remoteDir.path() + path;
0104 
0105         QByteArray data("testBasicGet");
0106         QFile file(remotePath);
0107         QVERIFY(file.open(QFile::WriteOnly));
0108         file.write(data);
0109         file.close();
0110 
0111         auto job = KIO::storedGet(url);
0112         job->setUiDelegate(nullptr);
0113         QVERIFY2(job->exec(), qUtf8Printable(job->errorString()));
0114         QCOMPARE(job->data(), data);
0115     }
0116 
0117     void testCopy()
0118     {
0119         const QString path("/testCopy");
0120         const auto url = this->url(path);
0121         const QString remotePath = m_remoteDir.path() + path;
0122         const QString partPath = remotePath + ".part";
0123 
0124         QFile::remove(remotePath);
0125         QFile::remove(partPath);
0126 
0127         const QString testCopy1 = QFINDTESTDATA("ftp/testCopy1");
0128         QVERIFY(!testCopy1.isEmpty());
0129         auto job = KIO::copy({QUrl::fromLocalFile(testCopy1)}, url, KIO::DefaultFlags);
0130         job->setUiDelegate(nullptr);
0131         QVERIFY2(job->exec(), qUtf8Printable(job->errorString()));
0132         QFile file(remotePath);
0133         QVERIFY(file.open(QFile::ReadOnly));
0134         QCOMPARE(file.readAll(), QByteArray("part1\n"));
0135     }
0136 
0137     void testCopyResume()
0138     {
0139         const QString path("/testCopy");
0140         const auto url = this->url(path);
0141         const QString remotePath = m_remoteDir.path() + path;
0142         const QString partPath = remotePath + ".part";
0143 
0144         QFile::remove(remotePath);
0145         QFile::remove(partPath);
0146         const QString testCopy1 = QFINDTESTDATA("ftp/testCopy1");
0147         QVERIFY(!testCopy1.isEmpty());
0148         QVERIFY(QFile::copy(testCopy1, partPath));
0149 
0150         const QString testCopy2 = QFINDTESTDATA("ftp/testCopy2");
0151         QVERIFY(!testCopy2.isEmpty());
0152         auto job = KIO::copy({QUrl::fromLocalFile(testCopy2)}, url, KIO::Resume);
0153         job->setUiDelegate(nullptr);
0154         QVERIFY2(job->exec(), qUtf8Printable(job->errorString()));
0155         QFile file(remotePath);
0156         QVERIFY(file.open(QFile::ReadOnly));
0157         QCOMPARE(file.readAll(), QByteArray("part1\npart2\n"));
0158     }
0159 
0160     void testOverwriteCopy()
0161     {
0162         const QString path("/testOverwriteCopy");
0163         const auto url = this->url(path);
0164         const QString remotePath = m_remoteDir.path() + path;
0165 
0166         qDebug() << (m_remoteDir.path() + path);
0167 
0168         // Create file
0169         const QString testCopy1 = QFINDTESTDATA("ftp/testCopy1");
0170         QVERIFY(!testCopy1.isEmpty());
0171         auto job1 = KIO::copy({QUrl::fromLocalFile(testCopy1)}, url, KIO::DefaultFlags);
0172         job1->setUiDelegate(nullptr);
0173         QVERIFY2(job1->exec(), qUtf8Printable(job1->errorString()));
0174         QFile file(remotePath);
0175         QVERIFY(file.open(QFile::ReadOnly));
0176         QCOMPARE(file.readAll(), QByteArray("part1\n"));
0177         file.close();
0178 
0179         // File already exists, we expect it to be overwritten.
0180         const QString testOverwriteCopy2 = QFINDTESTDATA("ftp/testOverwriteCopy2");
0181         QVERIFY(!testOverwriteCopy2.isEmpty());
0182         auto job2 = KIO::copy({QUrl::fromLocalFile(testOverwriteCopy2)}, url, KIO::Overwrite);
0183         job2->setUiDelegate(nullptr);
0184         QVERIFY2(job2->exec(), qUtf8Printable(job2->errorString()));
0185         QVERIFY(file.open(QFile::ReadOnly));
0186         QCOMPARE(file.readAll(), QByteArray("testOverwriteCopy2\n"));
0187     }
0188 
0189     void testOverwriteCopyWithoutFlagFromLocal()
0190     {
0191         const QString path("/testOverwriteCopyWithoutFlag");
0192         const auto url = this->url(path);
0193 
0194         qDebug() << (m_remoteDir.path() + path);
0195         const QString testOverwriteCopy1 = QFINDTESTDATA("ftp/testOverwriteCopy1");
0196         QVERIFY(!testOverwriteCopy1.isEmpty());
0197         QVERIFY(QFile::copy(testOverwriteCopy1, m_remoteDir.path() + path));
0198 
0199         // Without overwrite flag.
0200         const QString testOverwriteCopy2 = QFINDTESTDATA("ftp/testOverwriteCopy2");
0201         QVERIFY(!testOverwriteCopy2.isEmpty());
0202         auto job = KIO::copy({QUrl::fromLocalFile(testOverwriteCopy2)}, url, KIO::DefaultFlags);
0203         job->setUiDelegate(nullptr);
0204         QVERIFY2(!job->exec(), qUtf8Printable(job->errorString()));
0205         QCOMPARE(job->error(), KIO::ERR_FILE_ALREADY_EXIST);
0206         QFile file(m_remoteDir.path() + path);
0207         QVERIFY(file.open(QFile::ReadOnly));
0208         QCOMPARE(file.readAll(), QByteArray("testOverwriteCopy1\n")); // not 2!
0209     }
0210 
0211     void testOverwriteCopyWithoutFlagFromRemote()
0212     {
0213         // This exercises a different code path than testOverwriteCopyWithoutFlagFromLocal
0214         const QString path("/testOverwriteCopyWithoutFlagRemote");
0215         const QString dir_path("/dir");
0216         const auto url = this->url(path);
0217         const auto dir_url = this->url(dir_path);
0218 
0219         qDebug() << (m_remoteDir.path() + path);
0220         const auto testOverwriteCopy1 = QFINDTESTDATA("ftp/testOverwriteCopy1");
0221         QVERIFY(!testOverwriteCopy1.isEmpty());
0222         QVERIFY(QFile::copy(testOverwriteCopy1, m_remoteDir.path() + path));
0223         QVERIFY(QDir(m_remoteDir.path()).mkdir("dir"));
0224 
0225         // First copy should work.
0226         auto job = KIO::copy(url, dir_url, KIO::DefaultFlags);
0227         job->setUiDelegate(nullptr);
0228         QVERIFY2(job->exec(), qUtf8Printable(job->errorString()));
0229 
0230         // Without overwrite flag.
0231         auto job2 = KIO::copy(url, dir_url, KIO::DefaultFlags);
0232         job2->setUiDelegate(nullptr);
0233         QVERIFY2(!job2->exec(), qUtf8Printable(job2->errorString()));
0234         QCOMPARE(job2->error(), KIO::ERR_FILE_ALREADY_EXIST);
0235         QFile file(m_remoteDir.path() + path);
0236         QVERIFY(file.open(QFile::ReadOnly));
0237         QCOMPARE(file.readAll(), QByteArray("testOverwriteCopy1\n")); // not 2!
0238     }
0239 };
0240 
0241 QTEST_MAIN(WebDAVTest)
0242 #include "webdavtest.moc"