Changeset View
Changeset View
Standalone View
Standalone View
autotests/processlauncherjobtest.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | This file is part of the KDE libraries | ||||
3 | Copyright (c) 2014, 2020 David Faure <faure@kde.org> | ||||
4 | | ||||
5 | This library is free software; you can redistribute it and/or modify | ||||
6 | it under the terms of the GNU Lesser General Public License as published by | ||||
7 | the Free Software Foundation; either version 2 of the License or ( at | ||||
8 | your option ) version 3 or, at the discretion of KDE e.V. ( which shall | ||||
9 | act as a proxy as in section 14 of the GPLv3 ), any later version. | ||||
10 | | ||||
11 | This library is distributed in the hope that it will be useful, | ||||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
14 | Library General Public License for more details. | ||||
15 | | ||||
16 | You should have received a copy of the GNU Lesser General Public License | ||||
17 | along with this library; see the file COPYING.LIB. If not, write to | ||||
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
19 | Boston, MA 02110-1301, USA. | ||||
20 | */ | ||||
21 | | ||||
22 | #include "processlauncherjobtest.h" | ||||
23 | #include "processlauncherjob.h" | ||||
24 | | ||||
25 | #include "kiotesthelper.h" // createTestFile etc. | ||||
26 | | ||||
27 | #include <kservice.h> | ||||
28 | #include <kconfiggroup.h> | ||||
29 | #include <KDesktopFile> | ||||
30 | | ||||
31 | #ifdef Q_OS_UNIX | ||||
32 | #include <signal.h> // kill | ||||
33 | #endif | ||||
34 | | ||||
35 | #include <QStandardPaths> | ||||
36 | #include <QTemporaryDir> | ||||
37 | #include <QTest> | ||||
38 | #include <kprocessrunner_p.h> | ||||
39 | | ||||
40 | QTEST_GUILESS_MAIN(ProcessLauncherJobTest) | ||||
41 | | ||||
42 | void ProcessLauncherJobTest::initTestCase() | ||||
43 | { | ||||
44 | QStandardPaths::setTestModeEnabled(true); | ||||
45 | } | ||||
46 | | ||||
47 | void ProcessLauncherJobTest::cleanupTestCase() | ||||
48 | { | ||||
49 | std::for_each(m_filesToRemove.begin(), m_filesToRemove.end(), [](const QString & f) { | ||||
50 | QFile::remove(f); | ||||
51 | }); | ||||
52 | } | ||||
53 | | ||||
54 | static const char s_tempServiceName[] = "processlauncherjobtest_service.desktop"; | ||||
55 | | ||||
56 | static void createSrcFile(const QString path) | ||||
57 | { | ||||
58 | QFile srcFile(path); | ||||
59 | QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString())); | ||||
60 | srcFile.write("Hello world\n"); | ||||
61 | } | ||||
62 | | ||||
63 | void ProcessLauncherJobTest::startProcess_data() | ||||
64 | { | ||||
65 | QTest::addColumn<bool>("tempFile"); | ||||
66 | QTest::addColumn<bool>("useExec"); | ||||
67 | QTest::addColumn<int>("numFiles"); | ||||
68 | | ||||
69 | QTest::newRow("1_file_exec") << false << true << 1; | ||||
70 | QTest::newRow("1_file_waitForStarted") << false << false << 1; | ||||
71 | QTest::newRow("1_tempfile_exec") << true << true << 1; | ||||
72 | QTest::newRow("1_tempfile_waitForStarted") << true << false << 1; | ||||
73 | | ||||
74 | QTest::newRow("2_files_exec") << false << true << 2; | ||||
75 | QTest::newRow("2_files_waitForStarted") << false << false << 2; | ||||
76 | QTest::newRow("2_tempfiles_exec") << true << true << 2; | ||||
77 | QTest::newRow("2_tempfiles_waitForStarted") << true << false << 2; | ||||
78 | } | ||||
79 | | ||||
80 | void ProcessLauncherJobTest::startProcess() | ||||
81 | { | ||||
82 | QFETCH(bool, tempFile); | ||||
83 | QFETCH(bool, useExec); | ||||
84 | QFETCH(int, numFiles); | ||||
85 | | ||||
86 | // Given a service desktop file and a number of source files | ||||
87 | const QString path = createTempService(); | ||||
88 | QTemporaryDir tempDir; | ||||
89 | const QString srcDir = tempDir.path(); | ||||
90 | QList<QUrl> urls; | ||||
91 | for (int i = 0; i < numFiles; ++i) { | ||||
92 | const QString srcFile = srcDir + "/srcfile" + QString::number(i + 1); | ||||
93 | createSrcFile(srcFile); | ||||
94 | QVERIFY(QFile::exists(srcFile)); | ||||
95 | urls.append(QUrl::fromLocalFile(srcFile)); | ||||
96 | } | ||||
97 | | ||||
98 | // When running a ProcessLauncherJob | ||||
99 | KService::Ptr servicePtr(new KService(path)); | ||||
100 | KIO::ProcessLauncherJob *job = new KIO::ProcessLauncherJob(servicePtr, WId{}, this); | ||||
101 | job->setUrls(urls); | ||||
102 | if (tempFile) { | ||||
103 | job->setRunFlags(KIO::ProcessLauncherJob::DeleteTemporaryFiles); | ||||
104 | } | ||||
105 | if (useExec) { | ||||
106 | QVERIFY(job->exec()); | ||||
107 | } else { | ||||
108 | job->start(); | ||||
109 | QVERIFY(job->waitForStarted()); | ||||
110 | } | ||||
111 | const QVector<qint64> pids = job->pids(); | ||||
112 | | ||||
113 | // Then the service should be executed (which copies the source file to "dest") | ||||
114 | QCOMPARE(pids.count(), numFiles); | ||||
115 | QVERIFY(!pids.contains(0)); | ||||
116 | for (int i = 0; i < numFiles; ++i) { | ||||
117 | const QString dest = srcDir + "/dest_srcfile" + QString::number(i + 1); | ||||
118 | QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest)); | ||||
119 | QVERIFY(QFile::exists(srcDir + "/srcfile" + QString::number(i + 1))); // if tempfile is true, kioexec will delete it... in 3 minutes. | ||||
120 | QVERIFY(QFile::remove(dest)); // cleanup | ||||
121 | } | ||||
122 | | ||||
123 | #ifdef Q_OS_UNIX | ||||
124 | // Kill the running kioexec processes | ||||
125 | for (qint64 pid : pids) { | ||||
126 | ::kill(pid, SIGTERM); | ||||
127 | } | ||||
128 | #endif | ||||
129 | | ||||
130 | // The kioexec processes that are waiting for 3 minutes and got killed above, | ||||
131 | // will now trigger KProcessRunner::slotProcessError, KProcessRunner::slotProcessExited and delete the KProcessRunner. | ||||
132 | // We wait for that to happen otherwise it gets confusing to see that output from later tests. | ||||
133 | QTRY_COMPARE(KProcessRunner::instanceCount(), 0); | ||||
134 | } | ||||
135 | | ||||
136 | void ProcessLauncherJobTest::shouldFailOnNonExecutableDesktopFile() | ||||
137 | { | ||||
138 | // Given a .desktop file in a temporary directory (outside the trusted paths) | ||||
139 | QTemporaryDir tempDir; | ||||
140 | const QString srcDir = tempDir.path(); | ||||
141 | const QString desktopFilePath = srcDir + "/shouldfail.desktop"; | ||||
142 | writeTempServiceDesktopFile(desktopFilePath); | ||||
143 | m_filesToRemove.append(desktopFilePath); | ||||
144 | | ||||
145 | const QString srcFile = srcDir + "/srcfile"; | ||||
146 | createSrcFile(srcFile); | ||||
147 | const QList<QUrl> urls{QUrl::fromLocalFile(srcFile)}; | ||||
148 | KService::Ptr servicePtr(new KService(desktopFilePath)); | ||||
149 | KIO::ProcessLauncherJob *job = new KIO::ProcessLauncherJob(servicePtr, WId{}, this); | ||||
150 | job->setUrls(urls); | ||||
151 | QVERIFY(!job->exec()); | ||||
152 | QCOMPARE(job->error(), KJob::UserDefinedError); | ||||
153 | QCOMPARE(job->errorString(), QStringLiteral("You are not authorized to execute this file.")); | ||||
154 | } | ||||
155 | | ||||
156 | void ProcessLauncherJobTest::shouldFailOnNonExistingExecutable_data() | ||||
157 | { | ||||
158 | QTest::addColumn<bool>("tempFile"); | ||||
159 | | ||||
160 | QTest::newRow("file") << false; | ||||
161 | QTest::newRow("tempFile") << true; | ||||
162 | } | ||||
163 | | ||||
164 | void ProcessLauncherJobTest::shouldFailOnNonExistingExecutable() | ||||
165 | { | ||||
166 | QFETCH(bool, tempFile); | ||||
167 | | ||||
168 | const QString desktopFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/non_existing_executable.desktop"); | ||||
169 | KDesktopFile file(desktopFilePath); | ||||
170 | KConfigGroup group = file.desktopGroup(); | ||||
171 | group.writeEntry("Name", "KRunUnittestService"); | ||||
172 | group.writeEntry("Type", "Service"); | ||||
173 | group.writeEntry("Exec", "does_not_exist %f %d/dest_%n"); | ||||
174 | file.sync(); | ||||
175 | | ||||
176 | KService::Ptr servicePtr(new KService(desktopFilePath)); | ||||
177 | KIO::ProcessLauncherJob *job = new KIO::ProcessLauncherJob(servicePtr, WId{}, this); | ||||
178 | job->setUrls({QUrl::fromLocalFile(desktopFilePath)}); // just to have one URL as argument, as the desktop file expects | ||||
179 | if (tempFile) { | ||||
180 | job->setRunFlags(KIO::ProcessLauncherJob::DeleteTemporaryFiles); | ||||
181 | } | ||||
182 | QVERIFY(!job->exec()); | ||||
183 | QCOMPARE(job->error(), KJob::UserDefinedError); | ||||
184 | QCOMPARE(job->errorString(), QStringLiteral("Could not find the program 'does_not_exist'")); | ||||
185 | | ||||
186 | QFile::remove(desktopFilePath); | ||||
187 | } | ||||
188 | | ||||
189 | void ProcessLauncherJobTest::writeTempServiceDesktopFile(const QString &filePath) | ||||
190 | { | ||||
191 | if (!QFile::exists(filePath)) { | ||||
192 | KDesktopFile file(filePath); | ||||
193 | KConfigGroup group = file.desktopGroup(); | ||||
194 | group.writeEntry("Name", "KRunUnittestService"); | ||||
195 | group.writeEntry("Type", "Service"); | ||||
196 | #ifdef Q_OS_WIN | ||||
197 | group.writeEntry("Exec", "copy.exe %f %d/dest_%n"); | ||||
198 | #else | ||||
199 | group.writeEntry("Exec", "cd %d ; cp %f %d/dest_%n"); // cd is just to show that we can't do QFile::exists(binary) | ||||
200 | #endif | ||||
201 | file.sync(); | ||||
202 | } | ||||
203 | } | ||||
204 | | ||||
205 | QString ProcessLauncherJobTest::createTempService() | ||||
206 | { | ||||
207 | const QString fileName = s_tempServiceName; | ||||
208 | const QString fakeService = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + fileName; | ||||
209 | writeTempServiceDesktopFile(fakeService); | ||||
210 | m_filesToRemove.append(fakeService); | ||||
211 | return fakeService; | ||||
212 | } |