File indexing completed on 2024-04-14 14:23:53

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "commandlauncherjobtest.h"
0009 #include "commandlauncherjob.h"
0010 
0011 #include "kiotesthelper.h" // createTestFile etc.
0012 
0013 #include <QStandardPaths>
0014 #include <QTemporaryDir>
0015 #include <QTest>
0016 #include <kprocessrunner_p.h>
0017 
0018 QTEST_GUILESS_MAIN(CommandLauncherJobTest)
0019 
0020 void CommandLauncherJobTest::initTestCase()
0021 {
0022     QStandardPaths::setTestModeEnabled(true);
0023 }
0024 
0025 static void createSrcFile(const QString path)
0026 {
0027     QFile srcFile(path);
0028     QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString()));
0029     QVERIFY(srcFile.write("Hello world\n") > 0);
0030 }
0031 
0032 void CommandLauncherJobTest::startProcessAsCommand_data()
0033 {
0034     QTest::addColumn<bool>("useExec");
0035 
0036     QTest::newRow("exec") << true;
0037     QTest::newRow("waitForStarted") << false;
0038 }
0039 
0040 void CommandLauncherJobTest::startProcessAsCommand()
0041 {
0042     QFETCH(bool, useExec);
0043 
0044     // Given a command
0045     QTemporaryDir tempDir;
0046     const QString srcDir = tempDir.path();
0047     const QString srcFile = srcDir + "/srcfile";
0048     createSrcFile(srcFile);
0049     QVERIFY(QFile::exists(srcFile));
0050 
0051 #ifdef Q_OS_WIN
0052     QString command = "copy.exe";
0053 #else
0054     QString command = "cp";
0055 #endif
0056 
0057     command += QStringLiteral(" %1 destfile").arg(srcFile);
0058 
0059     // When running a CommandLauncherJob
0060     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
0061     job->setWorkingDirectory(srcDir);
0062     if (useExec) {
0063         QVERIFY(job->exec());
0064     } else {
0065         job->start();
0066         QVERIFY(job->waitForStarted());
0067     }
0068     const qint64 pid = job->pid();
0069 
0070     // Then the service should be executed (which copies the source file to "dest")
0071     QVERIFY(pid != 0);
0072     const QString dest = srcDir + "/destfile";
0073     QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest));
0074     QVERIFY(QFile::remove(srcFile)); // cleanup
0075     QVERIFY(QFile::remove(dest));
0076 
0077     // Just to make sure
0078     QTRY_COMPARE(KProcessRunner::instanceCount(), 0);
0079 }
0080 
0081 void CommandLauncherJobTest::startProcessWithArgs_data()
0082 {
0083     QTest::addColumn<QString>("srcName");
0084     QTest::addColumn<QString>("destName");
0085 
0086     QTest::newRow("path without spaces") << QStringLiteral("srcfile") << QStringLiteral("destfile");
0087     QTest::newRow("path with spaces") << QStringLiteral("Source File") << QStringLiteral("Destination File");
0088 }
0089 
0090 void CommandLauncherJobTest::startProcessWithArgs()
0091 {
0092     QFETCH(QString, srcName);
0093     QFETCH(QString, destName);
0094 
0095     QTemporaryDir tempDir;
0096     const QString srcDir = tempDir.path();
0097     const QString srcPath = srcDir + '/' + srcName;
0098     const QString destPath = srcDir + '/' + destName;
0099 
0100     createSrcFile(srcPath);
0101     QVERIFY(QFileInfo::exists(srcPath));
0102 
0103 #ifdef Q_OS_WIN
0104     const QString executable = "copy.exe";
0105 #else
0106     const QString executable = "cp";
0107 #endif
0108 
0109     auto *job = new KIO::CommandLauncherJob(executable, {srcPath, destName}, this);
0110     job->setWorkingDirectory(srcDir);
0111 
0112     job->start();
0113     QVERIFY(job->waitForStarted());
0114 
0115     const qint64 pid = job->pid();
0116 
0117     // Then the service should be executed (which copies the source file to "dest")
0118     QVERIFY(pid != 0);
0119     QTRY_VERIFY2(QFileInfo::exists(destPath), qPrintable(destPath));
0120     QVERIFY(QFile::remove(srcPath));
0121     QVERIFY(QFile::remove(destPath)); // cleanup
0122 
0123     // Just to make sure
0124     QTRY_COMPARE(KProcessRunner::instanceCount(), 0);
0125 }
0126 
0127 void CommandLauncherJobTest::startProcessWithSpacesInExecutablePath_data()
0128 {
0129     QTest::addColumn<QString>("srcName");
0130     QTest::addColumn<QString>("destName");
0131 
0132     QTest::newRow("path without spaces") << QStringLiteral("srcfile") << QStringLiteral("destfile");
0133     QTest::newRow("path with spaces") << QStringLiteral("Source File") << QStringLiteral("Destination File");
0134 }
0135 
0136 void CommandLauncherJobTest::startProcessWithSpacesInExecutablePath()
0137 {
0138     QFETCH(QString, srcName);
0139     QFETCH(QString, destName);
0140 
0141     QTemporaryDir tempDir;
0142 
0143     const QString srcDir = tempDir.filePath("folder with spaces");
0144     QVERIFY(QDir().mkpath(srcDir));
0145 
0146     const QString srcPath = srcDir + '/' + srcName;
0147     const QString destPath = srcDir + '/' + destName;
0148 
0149     createSrcFile(srcPath);
0150     QVERIFY(QFileInfo::exists(srcPath));
0151 
0152     // Copy the executable into the folder with spaces in its path
0153 #ifdef Q_OS_WIN
0154     const QString executableName = "copy"; // QStandardPaths appends extension as necessary
0155 #else
0156     const QString executableName = "cp";
0157 #endif
0158 
0159     const QString executablePath = QStandardPaths::findExecutable(executableName);
0160     QVERIFY(!executablePath.isEmpty());
0161     QFileInfo fi(executablePath);
0162     // Needed since it could be .exe or .bat on Windows
0163     const QString executableFileName = fi.fileName();
0164 
0165     const QString executable = srcDir + '/' + executableFileName;
0166     QVERIFY(QFile::copy(executablePath, executable));
0167 
0168     auto *job = new KIO::CommandLauncherJob(executable, {srcPath, destName}, this);
0169     job->setWorkingDirectory(srcDir);
0170 
0171     job->start();
0172     QVERIFY(job->waitForStarted());
0173 
0174     const qint64 pid = job->pid();
0175 
0176     // Then the service should be executed (which copies the source file to "dest")
0177     QVERIFY(pid != 0);
0178     QTRY_VERIFY2(QFileInfo::exists(destPath), qPrintable(destPath));
0179 
0180     // cleanup
0181     QVERIFY(QFile::remove(destPath));
0182     QVERIFY(QFile::remove(srcPath));
0183     QVERIFY(QFile::remove(executable));
0184 
0185     // Just to make sure
0186     QTRY_COMPARE(KProcessRunner::instanceCount(), 0);
0187 }
0188 
0189 void CommandLauncherJobTest::startProcessWithEnvironmentVariables()
0190 {
0191     // Given an env var and a command that uses it
0192     QProcessEnvironment env;
0193     env.insert("MYVAR", "myvalue");
0194 #ifdef Q_OS_WIN
0195     const QString command = "echo myvar=%MYVAR% > destfile";
0196 #else
0197     const QString command = "echo myvar=$MYVAR > destfile";
0198 #endif
0199     QTemporaryDir tempDir;
0200     const QString srcDir = tempDir.path();
0201     const QString srcFile = srcDir + "/srcfile";
0202     createSrcFile(srcFile);
0203 
0204     // When running a CommandLauncherJob
0205     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
0206     job->setWorkingDirectory(srcDir);
0207     job->setProcessEnvironment(env);
0208     QVERIFY(job->exec());
0209 
0210     // Then the env var was visible
0211     QFile destFile(srcDir + "/destfile");
0212     QTRY_VERIFY(QFileInfo(destFile).size() > 0);
0213     QVERIFY(destFile.open(QIODevice::ReadOnly));
0214     const QByteArray data = destFile.readAll().trimmed();
0215     QCOMPARE(data, "myvar=myvalue");
0216 }
0217 
0218 void CommandLauncherJobTest::doesNotFailOnNonExistingExecutable()
0219 {
0220     // Given a command that uses an executable that doesn't exist
0221     const QString command = "does_not_exist foo bar";
0222 
0223     // When running a CommandLauncherJob
0224     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this);
0225     job->setExecutable("really_does_not_exist");
0226 
0227     // Then it doesn't actually fail. QProcess is starting /bin/sh, which works...
0228     QVERIFY(job->exec());
0229 
0230     // Wait for KProcessRunner to be deleted
0231     QTRY_COMPARE(KProcessRunner::instanceCount(), 0);
0232 }
0233 
0234 void CommandLauncherJobTest::shouldDoNothingOnEmptyCommand()
0235 {
0236     // When running an empty command
0237     KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(QString(), this);
0238 
0239     // THEN it should do nothing
0240     // at least not crash (old bug 186036)
0241     QVERIFY(job->exec());
0242 
0243     // Wait for KProcessRunner to be deleted
0244     QTRY_COMPARE(KProcessRunner::instanceCount(), 0);
0245 }
0246 
0247 #include "moc_commandlauncherjobtest.cpp"