1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2021 The Qt Company Ltd. |
4 | ** Copyright (C) 2022 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <emulationdetector.h> |
31 | |
32 | #include <QtTest/QtTest> |
33 | #include <QtCore/QProcess> |
34 | #include <QtCore/QDir> |
35 | #include <QtCore/QElapsedTimer> |
36 | #include <QtCore/QFile> |
37 | #include <QtCore/QThread> |
38 | #include <QtCore/QTemporaryDir> |
39 | #include <QtCore/QRegExp> |
40 | #include <QtCore/QDebug> |
41 | #include <QtCore/QMetaType> |
42 | #include <QtCore/QScopeGuard> |
43 | #include <QtNetwork/QHostInfo> |
44 | |
45 | #include <qplatformdefs.h> |
46 | #ifdef Q_OS_UNIX |
47 | # include <private/qcore_unix_p.h> |
48 | #endif |
49 | |
50 | #include <stdlib.h> |
51 | |
52 | typedef void (QProcess::*QProcessFinishedSignal1)(int); |
53 | typedef void (QProcess::*QProcessFinishedSignal2)(int, QProcess::ExitStatus); |
54 | typedef void (QProcess::*QProcessErrorSignal)(QProcess::ProcessError); |
55 | |
56 | class tst_QProcess : public QObject |
57 | { |
58 | Q_OBJECT |
59 | |
60 | public slots: |
61 | void initTestCase(); |
62 | void cleanupTestCase(); |
63 | void init(); |
64 | |
65 | private slots: |
66 | void getSetCheck(); |
67 | void constructing(); |
68 | void simpleStart(); |
69 | void setupChildProcess(); |
70 | void startWithOpen(); |
71 | void startWithOldOpen(); |
72 | void execute(); |
73 | void startDetached(); |
74 | void crashTest(); |
75 | void crashTest2(); |
76 | void echoTest_data(); |
77 | void echoTest(); |
78 | void echoTest2(); |
79 | #ifdef Q_OS_WIN |
80 | void echoTestGui(); |
81 | void testSetNamedPipeHandleState(); |
82 | void batFiles_data(); |
83 | void batFiles(); |
84 | #endif |
85 | void loopBackTest(); |
86 | void readTimeoutAndThenCrash(); |
87 | void deadWhileReading(); |
88 | void restartProcessDeadlock(); |
89 | void closeWriteChannel(); |
90 | void closeReadChannel(); |
91 | void openModes(); |
92 | void emitReadyReadOnlyWhenNewDataArrives(); |
93 | void softExitInSlots_data(); |
94 | void softExitInSlots(); |
95 | void mergedChannels(); |
96 | void forwardedChannels_data(); |
97 | void forwardedChannels(); |
98 | void atEnd(); |
99 | void atEnd2(); |
100 | void waitForFinishedWithTimeout(); |
101 | void waitForReadyReadInAReadyReadSlot(); |
102 | void waitForBytesWrittenInABytesWrittenSlot(); |
103 | void setEnvironment_data(); |
104 | void setEnvironment(); |
105 | void setProcessEnvironment_data(); |
106 | void setProcessEnvironment(); |
107 | void environmentIsSorted(); |
108 | void spaceInName(); |
109 | void setStandardInputFile(); |
110 | void setStandardOutputFile_data(); |
111 | void setStandardOutputFile(); |
112 | void setStandardOutputFileNullDevice(); |
113 | void setStandardOutputFileAndWaitForBytesWritten(); |
114 | void setStandardOutputProcess_data(); |
115 | void setStandardOutputProcess(); |
116 | void removeFileWhileProcessIsRunning(); |
117 | void fileWriterProcess(); |
118 | void switchReadChannels(); |
119 | void discardUnwantedOutput(); |
120 | void setWorkingDirectory(); |
121 | void setNonExistentWorkingDirectory(); |
122 | |
123 | void exitStatus_data(); |
124 | void exitStatus(); |
125 | void waitForFinished(); |
126 | void hardExit(); |
127 | void softExit(); |
128 | void processInAThread(); |
129 | void processesInMultipleThreads(); |
130 | void spaceArgsTest_data(); |
131 | void spaceArgsTest(); |
132 | #if defined(Q_OS_WIN) |
133 | void nativeArguments(); |
134 | void createProcessArgumentsModifier(); |
135 | #endif // Q_OS_WIN |
136 | void exitCodeTest(); |
137 | void systemEnvironment(); |
138 | void lockupsInStartDetached(); |
139 | void waitForReadyReadForNonexistantProcess(); |
140 | void detachedProcessParameters_data(); |
141 | void detachedProcessParameters(); |
142 | void startFinishStartFinish(); |
143 | void invalidProgramString_data(); |
144 | void invalidProgramString(); |
145 | void onlyOneStartedSignal(); |
146 | void finishProcessBeforeReadingDone(); |
147 | void waitForStartedWithoutStart(); |
148 | void startStopStartStop(); |
149 | void startStopStartStopBuffers_data(); |
150 | void startStopStartStopBuffers(); |
151 | void processEventsInAReadyReadSlot_data(); |
152 | void processEventsInAReadyReadSlot(); |
153 | void startFromCurrentWorkingDir_data(); |
154 | void startFromCurrentWorkingDir(); |
155 | |
156 | // keep these at the end, since they use lots of processes and sometimes |
157 | // caused obscure failures to occur in tests that followed them (esp. on the Mac) |
158 | void failToStart(); |
159 | void failToStartWithWait(); |
160 | void failToStartWithEventLoop(); |
161 | void failToStartEmptyArgs_data(); |
162 | void failToStartEmptyArgs(); |
163 | |
164 | #if QT_DEPRECATED_SINCE(5, 13) |
165 | void crashTest2_deprecated(); |
166 | void restartProcessDeadlock_deprecated(); |
167 | void waitForReadyReadInAReadyReadSlot_deprecated(); |
168 | void finishProcessBeforeReadingDone_deprecated(); |
169 | #endif |
170 | |
171 | protected slots: |
172 | void readFromProcess(); |
173 | void exitLoopSlot(); |
174 | void processApplicationEvents(); |
175 | void restartProcess(); |
176 | void waitForReadyReadInAReadyReadSlotSlot(); |
177 | void waitForBytesWrittenInABytesWrittenSlotSlot(); |
178 | |
179 | private: |
180 | qint64 bytesAvailable; |
181 | QTemporaryDir m_temporaryDir; |
182 | }; |
183 | |
184 | void tst_QProcess::initTestCase() |
185 | { |
186 | QVERIFY2(m_temporaryDir.isValid(), qPrintable(m_temporaryDir.errorString())); |
187 | // chdir to our testdata path and execute helper apps relative to that. |
188 | QString testdata_dir = QFileInfo(QFINDTESTDATA("testProcessNormal" )).absolutePath(); |
189 | QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir)); |
190 | } |
191 | |
192 | void tst_QProcess::cleanupTestCase() |
193 | { |
194 | } |
195 | |
196 | void tst_QProcess::init() |
197 | { |
198 | bytesAvailable = 0; |
199 | } |
200 | |
201 | // Testing get/set functions |
202 | void tst_QProcess::getSetCheck() |
203 | { |
204 | QProcess obj1; |
205 | // ProcessChannelMode QProcess::readChannelMode() |
206 | // void QProcess::setProcessChannelMode(ProcessChannelMode) |
207 | #if QT_DEPRECATED_SINCE(5, 13) |
208 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::SeparateChannels)); |
209 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::SeparateChannels), obj1.readChannelMode()); |
210 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::MergedChannels)); |
211 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::MergedChannels), obj1.readChannelMode()); |
212 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::ForwardedChannels)); |
213 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::ForwardedChannels), obj1.readChannelMode()); |
214 | #endif |
215 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::SeparateChannels)); |
216 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::SeparateChannels), obj1.processChannelMode()); |
217 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::MergedChannels)); |
218 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::MergedChannels), obj1.processChannelMode()); |
219 | obj1.setProcessChannelMode(QProcess::ProcessChannelMode(QProcess::ForwardedChannels)); |
220 | QCOMPARE(QProcess::ProcessChannelMode(QProcess::ForwardedChannels), obj1.processChannelMode()); |
221 | |
222 | // ProcessChannel QProcess::readChannel() |
223 | // void QProcess::setReadChannel(ProcessChannel) |
224 | obj1.setReadChannel(QProcess::ProcessChannel(QProcess::StandardOutput)); |
225 | QCOMPARE(QProcess::ProcessChannel(QProcess::StandardOutput), obj1.readChannel()); |
226 | obj1.setReadChannel(QProcess::ProcessChannel(QProcess::StandardError)); |
227 | QCOMPARE(QProcess::ProcessChannel(QProcess::StandardError), obj1.readChannel()); |
228 | } |
229 | |
230 | void tst_QProcess::constructing() |
231 | { |
232 | QProcess process; |
233 | QCOMPARE(process.readChannel(), QProcess::StandardOutput); |
234 | QCOMPARE(process.workingDirectory(), QString()); |
235 | QCOMPARE(process.environment(), QStringList()); |
236 | QCOMPARE(process.error(), QProcess::UnknownError); |
237 | QCOMPARE(process.state(), QProcess::NotRunning); |
238 | QCOMPARE(process.processId(), 0); |
239 | QCOMPARE(process.readAllStandardOutput(), QByteArray()); |
240 | QCOMPARE(process.readAllStandardError(), QByteArray()); |
241 | QCOMPARE(process.canReadLine(), false); |
242 | |
243 | // QIODevice |
244 | QCOMPARE(process.openMode(), QIODevice::NotOpen); |
245 | QVERIFY(!process.isOpen()); |
246 | QVERIFY(!process.isReadable()); |
247 | QVERIFY(!process.isWritable()); |
248 | QVERIFY(process.isSequential()); |
249 | QCOMPARE(process.pos(), qlonglong(0)); |
250 | QCOMPARE(process.size(), qlonglong(0)); |
251 | QVERIFY(process.atEnd()); |
252 | QCOMPARE(process.bytesAvailable(), qlonglong(0)); |
253 | QCOMPARE(process.bytesToWrite(), qlonglong(0)); |
254 | QVERIFY(!process.errorString().isEmpty()); |
255 | |
256 | char c; |
257 | QCOMPARE(process.read(&c, 1), qlonglong(-1)); |
258 | QCOMPARE(process.write(&c, 1), qlonglong(-1)); |
259 | |
260 | QProcess proc2; |
261 | } |
262 | |
263 | void tst_QProcess::simpleStart() |
264 | { |
265 | qRegisterMetaType<QProcess::ProcessState>(typeName: "QProcess::ProcessState" ); |
266 | |
267 | QScopedPointer<QProcess> process(new QProcess); |
268 | QSignalSpy spy(process.data(), &QProcess::stateChanged); |
269 | QVERIFY(spy.isValid()); |
270 | connect(sender: process.data(), signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::readFromProcess); |
271 | |
272 | /* valgrind dislike SUID binaries(those that have the `s'-flag set), which |
273 | * makes it fail to start the process. For this reason utilities like `ping' won't |
274 | * start, when the auto test is run through `valgrind'. */ |
275 | process->start(command: "testProcessNormal/testProcessNormal" ); |
276 | if (process->state() != QProcess::Starting) |
277 | QCOMPARE(process->state(), QProcess::Running); |
278 | QVERIFY2(process->waitForStarted(5000), qPrintable(process->errorString())); |
279 | QCOMPARE(process->state(), QProcess::Running); |
280 | QTRY_COMPARE(process->state(), QProcess::NotRunning); |
281 | |
282 | process.reset(); |
283 | |
284 | QCOMPARE(spy.count(), 3); |
285 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(spy.at(0).at(0)), QProcess::Starting); |
286 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(spy.at(1).at(0)), QProcess::Running); |
287 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(spy.at(2).at(0)), QProcess::NotRunning); |
288 | } |
289 | |
290 | void tst_QProcess::setupChildProcess() |
291 | { |
292 | /* This test exists because in Qt 5.15, the Unix version of QProcess has |
293 | * some code that depends on whether it's an actual QProcess or a |
294 | * derived class */ |
295 | static const char setupChildMessage[] = "Called from setupChildProcess()" ; |
296 | class DerivedProcessClass : public QProcess { |
297 | public: |
298 | int fd; |
299 | DerivedProcessClass(int fd) : fd(fd) |
300 | { |
301 | } |
302 | |
303 | protected: |
304 | void setupChildProcess() override |
305 | { |
306 | QT_WRITE(fd, data: setupChildMessage, len: sizeof(setupChildMessage) - 1); |
307 | QT_CLOSE(fd); |
308 | } |
309 | }; |
310 | |
311 | int pipes[2] = { -1 , -1 }; |
312 | #ifdef Q_OS_UNIX |
313 | QVERIFY(qt_safe_pipe(pipes) == 0); |
314 | #endif |
315 | |
316 | DerivedProcessClass process(pipes[1]); |
317 | process.start(command: "testProcessNormal/testProcessNormal" ); |
318 | if (process.state() != QProcess::Starting) |
319 | QCOMPARE(process.state(), QProcess::Running); |
320 | QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); |
321 | |
322 | #ifdef Q_OS_UNIX |
323 | char buf[sizeof setupChildMessage] = {}; |
324 | qt_safe_close(fd: pipes[1]); |
325 | QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(setupChildMessage) - 1)); |
326 | QCOMPARE(buf, setupChildMessage); |
327 | qt_safe_close(fd: pipes[0]); |
328 | #endif |
329 | |
330 | QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); |
331 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
332 | QCOMPARE(process.exitCode(), 0); |
333 | } |
334 | |
335 | void tst_QProcess::startWithOpen() |
336 | { |
337 | QProcess p; |
338 | QTest::ignoreMessage(type: QtWarningMsg, message: "QProcess::start: program not set" ); |
339 | QCOMPARE(p.open(QIODevice::ReadOnly), false); |
340 | |
341 | p.setProgram("testProcessNormal/testProcessNormal" ); |
342 | QCOMPARE(p.program(), QString("testProcessNormal/testProcessNormal" )); |
343 | |
344 | p.setArguments(QStringList() << "arg1" << "arg2" ); |
345 | QCOMPARE(p.arguments().size(), 2); |
346 | |
347 | QVERIFY(p.open(QIODevice::ReadOnly)); |
348 | QCOMPARE(p.openMode(), QIODevice::ReadOnly); |
349 | QVERIFY(p.waitForFinished(5000)); |
350 | } |
351 | |
352 | void tst_QProcess::startWithOldOpen() |
353 | { |
354 | // similar to the above, but we start with start() actually |
355 | // while open() is overridden to call QIODevice::open(). |
356 | // This tests the BC requirement that "it works with the old implementation" |
357 | class OverriddenOpen : public QProcess |
358 | { |
359 | public: |
360 | virtual bool open(OpenMode mode) override |
361 | { return QIODevice::open(mode); } |
362 | }; |
363 | |
364 | OverriddenOpen p; |
365 | p.start(command: "testProcessNormal/testProcessNormal" ); |
366 | QVERIFY(p.waitForStarted(5000)); |
367 | QVERIFY(p.waitForFinished(5000)); |
368 | } |
369 | |
370 | void tst_QProcess::execute() |
371 | { |
372 | QCOMPARE(QProcess::execute("testProcessNormal/testProcessNormal" , |
373 | QStringList() << "arg1" << "arg2" ), 0); |
374 | QCOMPARE(QProcess::execute("nonexistingexe" ), -2); |
375 | } |
376 | |
377 | void tst_QProcess::startDetached() |
378 | { |
379 | QVERIFY(QProcess::startDetached("testProcessNormal/testProcessNormal" , |
380 | QStringList() << "arg1" << "arg2" )); |
381 | QCOMPARE(QProcess::startDetached("nonexistingexe" ), false); |
382 | } |
383 | |
384 | void tst_QProcess::readFromProcess() |
385 | { |
386 | QProcess *process = qobject_cast<QProcess *>(object: sender()); |
387 | QVERIFY(process); |
388 | int lines = 0; |
389 | while (process->canReadLine()) { |
390 | ++lines; |
391 | process->readLine(); |
392 | } |
393 | } |
394 | |
395 | void tst_QProcess::crashTest() |
396 | { |
397 | qRegisterMetaType<QProcess::ProcessState>(typeName: "QProcess::ProcessState" ); |
398 | QScopedPointer<QProcess> process(new QProcess); |
399 | QSignalSpy stateSpy(process.data(), &QProcess::stateChanged); |
400 | QVERIFY(stateSpy.isValid()); |
401 | process->start(command: "testProcessCrash/testProcessCrash" ); |
402 | QVERIFY(process->waitForStarted(5000)); |
403 | |
404 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
405 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
406 | |
407 | QSignalSpy spy(process.data(), &QProcess::errorOccurred); |
408 | QSignalSpy spy2(process.data(), static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
409 | QVERIFY(spy.isValid()); |
410 | QVERIFY(spy2.isValid()); |
411 | |
412 | #if QT_DEPRECATED_SINCE(5, 6) |
413 | QSignalSpy spy3(process.data(), static_cast<QProcessErrorSignal>(&QProcess::error)); |
414 | QVERIFY(spy3.isValid()); |
415 | #endif |
416 | |
417 | QVERIFY(process->waitForFinished(30000)); |
418 | |
419 | QCOMPARE(spy.count(), 1); |
420 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed); |
421 | |
422 | QCOMPARE(spy2.count(), 1); |
423 | QCOMPARE(*static_cast<const QProcess::ExitStatus *>(spy2.at(0).at(1).constData()), QProcess::CrashExit); |
424 | |
425 | #if QT_DEPRECATED_SINCE(5, 6) |
426 | QCOMPARE(spy3.count(), 1); |
427 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy3.at(0).at(0).constData()), QProcess::Crashed); |
428 | #endif |
429 | |
430 | QCOMPARE(process->exitStatus(), QProcess::CrashExit); |
431 | |
432 | // delete process; |
433 | process.reset(); |
434 | |
435 | QCOMPARE(stateSpy.count(), 3); |
436 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(stateSpy.at(0).at(0)), QProcess::Starting); |
437 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(stateSpy.at(1).at(0)), QProcess::Running); |
438 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(stateSpy.at(2).at(0)), QProcess::NotRunning); |
439 | } |
440 | |
441 | void tst_QProcess::crashTest2() |
442 | { |
443 | QProcess process; |
444 | process.start(command: "testProcessCrash/testProcessCrash" ); |
445 | QVERIFY(process.waitForStarted(5000)); |
446 | |
447 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
448 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
449 | |
450 | QSignalSpy spy(&process, static_cast<QProcessErrorSignal>(&QProcess::errorOccurred)); |
451 | QSignalSpy spy2(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
452 | |
453 | QVERIFY(spy.isValid()); |
454 | QVERIFY(spy2.isValid()); |
455 | |
456 | QObject::connect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
457 | receiver: this, slot: &tst_QProcess::exitLoopSlot); |
458 | |
459 | QTestEventLoop::instance().enterLoop(secs: 30); |
460 | if (QTestEventLoop::instance().timeout()) |
461 | QFAIL("Failed to detect crash : operation timed out" ); |
462 | |
463 | QCOMPARE(spy.count(), 1); |
464 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed); |
465 | |
466 | QCOMPARE(spy2.count(), 1); |
467 | QCOMPARE(*static_cast<const QProcess::ExitStatus *>(spy2.at(0).at(1).constData()), QProcess::CrashExit); |
468 | |
469 | QCOMPARE(process.exitStatus(), QProcess::CrashExit); |
470 | } |
471 | |
472 | void tst_QProcess::echoTest_data() |
473 | { |
474 | QTest::addColumn<QByteArray>(name: "input" ); |
475 | |
476 | QTest::newRow(dataTag: "1" ) << QByteArray("H" ); |
477 | QTest::newRow(dataTag: "2" ) << QByteArray("He" ); |
478 | QTest::newRow(dataTag: "3" ) << QByteArray("Hel" ); |
479 | QTest::newRow(dataTag: "4" ) << QByteArray("Hell" ); |
480 | QTest::newRow(dataTag: "5" ) << QByteArray("Hello" ); |
481 | QTest::newRow(dataTag: "100 bytes" ) << QByteArray(100, '@'); |
482 | QTest::newRow(dataTag: "1000 bytes" ) << QByteArray(1000, '@'); |
483 | QTest::newRow(dataTag: "10000 bytes" ) << QByteArray(10000, '@'); |
484 | } |
485 | |
486 | void tst_QProcess::echoTest() |
487 | { |
488 | QFETCH(QByteArray, input); |
489 | |
490 | QProcess process; |
491 | connect(sender: &process, signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::exitLoopSlot); |
492 | |
493 | process.start(command: "testProcessEcho/testProcessEcho" ); |
494 | QVERIFY(process.waitForStarted(5000)); |
495 | |
496 | process.write(data: input); |
497 | |
498 | QElapsedTimer stopWatch; |
499 | stopWatch.start(); |
500 | do { |
501 | QVERIFY(process.isOpen()); |
502 | QTestEventLoop::instance().enterLoop(secs: 2); |
503 | } while (stopWatch.elapsed() < 60000 && process.bytesAvailable() < input.size()); |
504 | if (stopWatch.elapsed() >= 60000) |
505 | QFAIL("Timed out" ); |
506 | |
507 | QByteArray message = process.readAll(); |
508 | QCOMPARE(message.size(), input.size()); |
509 | |
510 | char *c1 = message.data(); |
511 | char *c2 = input.data(); |
512 | while (*c1 && *c2) { |
513 | if (*c1 != *c2) |
514 | QCOMPARE(*c1, *c2); |
515 | ++c1; |
516 | ++c2; |
517 | } |
518 | QCOMPARE(*c1, *c2); |
519 | |
520 | process.write(data: "" , len: 1); |
521 | |
522 | QVERIFY(process.waitForFinished(5000)); |
523 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
524 | QCOMPARE(process.exitCode(), 0); |
525 | } |
526 | |
527 | void tst_QProcess::exitLoopSlot() |
528 | { |
529 | QTestEventLoop::instance().exitLoop(); |
530 | } |
531 | |
532 | void tst_QProcess::processApplicationEvents() |
533 | { |
534 | QCoreApplication::processEvents(); |
535 | } |
536 | |
537 | void tst_QProcess::echoTest2() |
538 | { |
539 | |
540 | QProcess process; |
541 | connect(sender: &process, signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::exitLoopSlot); |
542 | |
543 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
544 | QVERIFY(process.waitForStarted(5000)); |
545 | QVERIFY(!process.waitForReadyRead(250)); |
546 | QCOMPARE(process.error(), QProcess::Timedout); |
547 | |
548 | process.write(data: "Hello" ); |
549 | QSignalSpy spy0(&process, &QProcess::channelReadyRead); |
550 | QSignalSpy spy1(&process, &QProcess::readyReadStandardOutput); |
551 | QSignalSpy spy2(&process, &QProcess::readyReadStandardError); |
552 | |
553 | QVERIFY(spy0.isValid()); |
554 | QVERIFY(spy1.isValid()); |
555 | QVERIFY(spy2.isValid()); |
556 | |
557 | QElapsedTimer stopWatch; |
558 | stopWatch.start(); |
559 | forever { |
560 | QTestEventLoop::instance().enterLoop(secs: 1); |
561 | if (stopWatch.elapsed() >= 30000) |
562 | QFAIL("Timed out" ); |
563 | process.setReadChannel(QProcess::StandardOutput); |
564 | qint64 baso = process.bytesAvailable(); |
565 | |
566 | process.setReadChannel(QProcess::StandardError); |
567 | qint64 base = process.bytesAvailable(); |
568 | if (baso == 5 && base == 5) |
569 | break; |
570 | } |
571 | |
572 | QVERIFY(spy0.count() > 0); |
573 | QVERIFY(spy1.count() > 0); |
574 | QVERIFY(spy2.count() > 0); |
575 | |
576 | QCOMPARE(process.readAllStandardOutput(), QByteArray("Hello" )); |
577 | QCOMPARE(process.readAllStandardError(), QByteArray("Hello" )); |
578 | |
579 | process.write(data: "" , len: 1); |
580 | QVERIFY(process.waitForFinished(5000)); |
581 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
582 | QCOMPARE(process.exitCode(), 0); |
583 | } |
584 | |
585 | #if defined(Q_OS_WIN) |
586 | void tst_QProcess::echoTestGui() |
587 | { |
588 | QProcess process; |
589 | |
590 | process.start("testProcessEchoGui/testProcessEchoGui" ); |
591 | |
592 | |
593 | process.write("Hello" ); |
594 | process.write("q" ); |
595 | |
596 | QVERIFY(process.waitForFinished(50000)); |
597 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
598 | QCOMPARE(process.exitCode(), 0); |
599 | |
600 | QCOMPARE(process.readAllStandardOutput(), QByteArray("Hello" )); |
601 | QCOMPARE(process.readAllStandardError(), QByteArray("Hello" )); |
602 | } |
603 | |
604 | void tst_QProcess::testSetNamedPipeHandleState() |
605 | { |
606 | QProcess process; |
607 | process.setProcessChannelMode(QProcess::SeparateChannels); |
608 | process.start("testSetNamedPipeHandleState/testSetNamedPipeHandleState" ); |
609 | QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); |
610 | QVERIFY(process.waitForFinished(5000)); |
611 | QCOMPARE(process.exitCode(), 0); |
612 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
613 | } |
614 | #endif // Q_OS_WIN |
615 | |
616 | #if defined(Q_OS_WIN) |
617 | void tst_QProcess::batFiles_data() |
618 | { |
619 | QTest::addColumn<QString>("batFile" ); |
620 | QTest::addColumn<QByteArray>("output" ); |
621 | |
622 | QTest::newRow("simple" ) << QFINDTESTDATA("testBatFiles/simple.bat" ) << QByteArray("Hello" ); |
623 | QTest::newRow("with space" ) << QFINDTESTDATA("testBatFiles/with space.bat" ) << QByteArray("Hello" ); |
624 | } |
625 | |
626 | void tst_QProcess::batFiles() |
627 | { |
628 | QFETCH(QString, batFile); |
629 | QFETCH(QByteArray, output); |
630 | |
631 | QProcess proc; |
632 | |
633 | proc.start(batFile, QStringList()); |
634 | |
635 | QVERIFY(proc.waitForFinished(5000)); |
636 | QCOMPARE(proc.exitStatus(), QProcess::NormalExit); |
637 | QCOMPARE(proc.exitCode(), 0); |
638 | |
639 | QVERIFY(proc.bytesAvailable() > 0); |
640 | |
641 | QVERIFY(proc.readAll().startsWith(output)); |
642 | } |
643 | #endif // Q_OS_WIN |
644 | |
645 | void tst_QProcess::exitStatus_data() |
646 | { |
647 | QTest::addColumn<QStringList>(name: "processList" ); |
648 | QTest::addColumn<QList<QProcess::ExitStatus> >(name: "exitStatus" ); |
649 | |
650 | QTest::newRow(dataTag: "normal" ) << (QStringList() << "testProcessNormal/testProcessNormal" ) |
651 | << (QList<QProcess::ExitStatus>() << QProcess::NormalExit); |
652 | QTest::newRow(dataTag: "crash" ) << (QStringList() << "testProcessCrash/testProcessCrash" ) |
653 | << (QList<QProcess::ExitStatus>() << QProcess::CrashExit); |
654 | |
655 | QTest::newRow(dataTag: "normal-crash" ) << (QStringList() |
656 | << "testProcessNormal/testProcessNormal" |
657 | << "testProcessCrash/testProcessCrash" ) |
658 | << (QList<QProcess::ExitStatus>() |
659 | << QProcess::NormalExit |
660 | << QProcess::CrashExit); |
661 | QTest::newRow(dataTag: "crash-normal" ) << (QStringList() |
662 | << "testProcessCrash/testProcessCrash" |
663 | << "testProcessNormal/testProcessNormal" ) |
664 | << (QList<QProcess::ExitStatus>() |
665 | << QProcess::CrashExit |
666 | << QProcess::NormalExit); |
667 | } |
668 | |
669 | void tst_QProcess::exitStatus() |
670 | { |
671 | QProcess process; |
672 | QFETCH(QStringList, processList); |
673 | QFETCH(QList<QProcess::ExitStatus>, exitStatus); |
674 | |
675 | QCOMPARE(exitStatus.count(), processList.count()); |
676 | for (int i = 0; i < processList.count(); ++i) { |
677 | process.start(command: processList.at(i)); |
678 | QVERIFY(process.waitForStarted(5000)); |
679 | QVERIFY(process.waitForFinished(30000)); |
680 | |
681 | QCOMPARE(process.exitStatus(), exitStatus.at(i)); |
682 | } |
683 | } |
684 | |
685 | void tst_QProcess::loopBackTest() |
686 | { |
687 | |
688 | QProcess process; |
689 | process.start(command: "testProcessEcho/testProcessEcho" ); |
690 | QVERIFY(process.waitForStarted(5000)); |
691 | |
692 | for (int i = 0; i < 100; ++i) { |
693 | process.write(data: "Hello" ); |
694 | do { |
695 | QVERIFY(process.waitForReadyRead(5000)); |
696 | } while (process.bytesAvailable() < 5); |
697 | QCOMPARE(process.readAll(), QByteArray("Hello" )); |
698 | } |
699 | |
700 | process.write(data: "" , len: 1); |
701 | QVERIFY(process.waitForFinished(5000)); |
702 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
703 | QCOMPARE(process.exitCode(), 0); |
704 | } |
705 | |
706 | void tst_QProcess::readTimeoutAndThenCrash() |
707 | { |
708 | |
709 | QProcess process; |
710 | process.start(command: "testProcessEcho/testProcessEcho" ); |
711 | if (process.state() != QProcess::Starting) |
712 | QCOMPARE(process.state(), QProcess::Running); |
713 | |
714 | QVERIFY(process.waitForStarted(5000)); |
715 | QCOMPARE(process.state(), QProcess::Running); |
716 | |
717 | QVERIFY(!process.waitForReadyRead(5000)); |
718 | QCOMPARE(process.error(), QProcess::Timedout); |
719 | |
720 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
721 | QSignalSpy spy(&process, &QProcess::errorOccurred); |
722 | QVERIFY(spy.isValid()); |
723 | #if QT_DEPRECATED_SINCE(5, 6) |
724 | QSignalSpy spy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
725 | QVERIFY(spy2.isValid()); |
726 | #endif |
727 | |
728 | process.kill(); |
729 | |
730 | QVERIFY(process.waitForFinished(5000)); |
731 | QCOMPARE(process.state(), QProcess::NotRunning); |
732 | |
733 | QCOMPARE(spy.count(), 1); |
734 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed); |
735 | #if QT_DEPRECATED_SINCE(5, 6) |
736 | QCOMPARE(spy2.count(), 1); |
737 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy2.at(0).at(0).constData()), QProcess::Crashed); |
738 | #endif |
739 | } |
740 | |
741 | void tst_QProcess::waitForFinished() |
742 | { |
743 | QProcess process; |
744 | |
745 | process.start(command: "testProcessOutput/testProcessOutput" ); |
746 | |
747 | QVERIFY(process.waitForFinished()); |
748 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
749 | |
750 | QString output = process.readAll(); |
751 | QCOMPARE(output.count("\n" ), 10*1024); |
752 | |
753 | process.start(command: "blurdybloop" ); |
754 | QVERIFY(!process.waitForFinished()); |
755 | QCOMPARE(process.error(), QProcess::FailedToStart); |
756 | } |
757 | |
758 | void tst_QProcess::deadWhileReading() |
759 | { |
760 | QProcess process; |
761 | |
762 | process.start(command: "testProcessDeadWhileReading/testProcessDeadWhileReading" ); |
763 | |
764 | QString output; |
765 | |
766 | QVERIFY(process.waitForStarted(5000)); |
767 | while (process.waitForReadyRead(msecs: 5000)) |
768 | output += process.readAll(); |
769 | |
770 | QCOMPARE(output.count("\n" ), 10*1024); |
771 | process.waitForFinished(); |
772 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
773 | QCOMPARE(process.exitCode(), 0); |
774 | } |
775 | |
776 | void tst_QProcess::restartProcessDeadlock() |
777 | { |
778 | // The purpose of this test is to detect whether restarting a |
779 | // process in the finished() connected slot causes a deadlock |
780 | // because of the way QProcessManager uses its locks. |
781 | QProcess process; |
782 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
783 | receiver: this, slot: &tst_QProcess::restartProcess); |
784 | |
785 | process.start(command: "testProcessEcho/testProcessEcho" ); |
786 | |
787 | QCOMPARE(process.write("" , 1), qlonglong(1)); |
788 | QVERIFY(process.waitForFinished(5000)); |
789 | |
790 | QObject::disconnect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), receiver: nullptr, zero: nullptr); |
791 | |
792 | QCOMPARE(process.write("" , 1), qlonglong(1)); |
793 | QVERIFY(process.waitForFinished(5000)); |
794 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
795 | QCOMPARE(process.exitCode(), 0); |
796 | } |
797 | |
798 | void tst_QProcess::restartProcess() |
799 | { |
800 | QProcess *process = qobject_cast<QProcess *>(object: sender()); |
801 | QVERIFY(process); |
802 | process->start(command: "testProcessEcho/testProcessEcho" ); |
803 | } |
804 | |
805 | void tst_QProcess::closeWriteChannel() |
806 | { |
807 | QByteArray testData("Data to read" ); |
808 | QProcess more; |
809 | more.start(command: "testProcessEOF/testProcessEOF" ); |
810 | |
811 | QVERIFY(more.waitForStarted(5000)); |
812 | QVERIFY(!more.waitForReadyRead(250)); |
813 | QCOMPARE(more.error(), QProcess::Timedout); |
814 | |
815 | QCOMPARE(more.write(testData), qint64(testData.size())); |
816 | |
817 | QVERIFY(!more.waitForReadyRead(250)); |
818 | QCOMPARE(more.error(), QProcess::Timedout); |
819 | |
820 | more.closeWriteChannel(); |
821 | // During closeWriteChannel() call, we might also get an I/O completion |
822 | // on the read pipe. So, take this into account before waiting for |
823 | // the new incoming data. |
824 | while (more.bytesAvailable() < testData.size()) |
825 | QVERIFY(more.waitForReadyRead(5000)); |
826 | QCOMPARE(more.readAll(), testData); |
827 | |
828 | if (more.state() == QProcess::Running) |
829 | QVERIFY(more.waitForFinished(5000)); |
830 | QCOMPARE(more.exitStatus(), QProcess::NormalExit); |
831 | QCOMPARE(more.exitCode(), 0); |
832 | } |
833 | |
834 | void tst_QProcess::closeReadChannel() |
835 | { |
836 | for (int i = 0; i < 10; ++i) { |
837 | QProcess::ProcessChannel channel1 = QProcess::StandardOutput; |
838 | QProcess::ProcessChannel channel2 = QProcess::StandardError; |
839 | |
840 | QProcess proc; |
841 | proc.start(command: "testProcessEcho2/testProcessEcho2" ); |
842 | QVERIFY(proc.waitForStarted(5000)); |
843 | proc.closeReadChannel(channel: i&1 ? channel2 : channel1); |
844 | proc.setReadChannel(i&1 ? channel2 : channel1); |
845 | proc.write(data: "Data" ); |
846 | |
847 | QVERIFY(!proc.waitForReadyRead(5000)); |
848 | QVERIFY(proc.readAll().isEmpty()); |
849 | |
850 | proc.setReadChannel(i&1 ? channel1 : channel2); |
851 | |
852 | while (proc.bytesAvailable() < 4 && proc.waitForReadyRead(msecs: 5000)) |
853 | { } |
854 | |
855 | QCOMPARE(proc.readAll(), QByteArray("Data" )); |
856 | |
857 | proc.write(data: "" , len: 1); |
858 | QVERIFY(proc.waitForFinished(5000)); |
859 | QCOMPARE(proc.exitStatus(), QProcess::NormalExit); |
860 | QCOMPARE(proc.exitCode(), 0); |
861 | } |
862 | } |
863 | |
864 | void tst_QProcess::openModes() |
865 | { |
866 | QProcess proc; |
867 | QVERIFY(!proc.isOpen()); |
868 | QCOMPARE(proc.openMode(), QProcess::NotOpen); |
869 | proc.start(command: "testProcessEcho3/testProcessEcho3" ); |
870 | QVERIFY(proc.waitForStarted(5000)); |
871 | QVERIFY(proc.isOpen()); |
872 | QCOMPARE(proc.openMode(), QProcess::ReadWrite); |
873 | QVERIFY(proc.isReadable()); |
874 | QVERIFY(proc.isWritable()); |
875 | |
876 | proc.write(data: "Data" ); |
877 | |
878 | proc.closeWriteChannel(); |
879 | |
880 | QVERIFY(proc.isWritable()); |
881 | QCOMPARE(proc.openMode(), QProcess::ReadWrite); |
882 | |
883 | while (proc.bytesAvailable() < 4 && proc.waitForReadyRead(msecs: 5000)) |
884 | { } |
885 | |
886 | QCOMPARE(proc.readAll().constData(), QByteArray("Data" ).constData()); |
887 | |
888 | proc.closeReadChannel(channel: QProcess::StandardOutput); |
889 | |
890 | QCOMPARE(proc.openMode(), QProcess::ReadWrite); |
891 | QVERIFY(proc.isReadable()); |
892 | |
893 | proc.closeReadChannel(channel: QProcess::StandardError); |
894 | |
895 | QCOMPARE(proc.openMode(), QProcess::ReadWrite); |
896 | QVERIFY(proc.isReadable()); |
897 | |
898 | proc.close(); |
899 | QVERIFY(!proc.isOpen()); |
900 | QVERIFY(!proc.isReadable()); |
901 | QVERIFY(!proc.isWritable()); |
902 | QCOMPARE(proc.state(), QProcess::NotRunning); |
903 | } |
904 | |
905 | void tst_QProcess::emitReadyReadOnlyWhenNewDataArrives() |
906 | { |
907 | |
908 | QProcess proc; |
909 | connect(sender: &proc, signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::exitLoopSlot); |
910 | QSignalSpy spy(&proc, &QProcess::readyRead); |
911 | QVERIFY(spy.isValid()); |
912 | |
913 | proc.start(command: "testProcessEcho/testProcessEcho" ); |
914 | |
915 | QCOMPARE(spy.count(), 0); |
916 | |
917 | proc.write(data: "A" ); |
918 | |
919 | QTestEventLoop::instance().enterLoop(secs: 5); |
920 | if (QTestEventLoop::instance().timeout()) |
921 | QFAIL("Operation timed out" ); |
922 | |
923 | QCOMPARE(spy.count(), 1); |
924 | |
925 | QTestEventLoop::instance().enterLoop(secs: 1); |
926 | QVERIFY(QTestEventLoop::instance().timeout()); |
927 | QVERIFY(!proc.waitForReadyRead(250)); |
928 | |
929 | QObject::disconnect(sender: &proc, signal: &QIODevice::readyRead, receiver: nullptr, zero: nullptr); |
930 | proc.write(data: "B" ); |
931 | QVERIFY(proc.waitForReadyRead(5000)); |
932 | |
933 | proc.write(data: "" , len: 1); |
934 | QVERIFY(proc.waitForFinished(5000)); |
935 | QCOMPARE(proc.exitStatus(), QProcess::NormalExit); |
936 | QCOMPARE(proc.exitCode(), 0); |
937 | } |
938 | |
939 | void tst_QProcess::hardExit() |
940 | { |
941 | QProcess proc; |
942 | |
943 | proc.start(command: "testProcessEcho/testProcessEcho" ); |
944 | |
945 | QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString())); |
946 | |
947 | #if defined(Q_OS_QNX) |
948 | // QNX may lose the kill if it's delivered while the forked process |
949 | // is doing the exec that morphs it into testProcessEcho. It's very |
950 | // unlikely that a normal application would do such a thing. Make |
951 | // sure the test doesn't accidentally try to do it. |
952 | proc.write("A" ); |
953 | QVERIFY(proc.waitForReadyRead(5000)); |
954 | #endif |
955 | |
956 | proc.kill(); |
957 | |
958 | QVERIFY(proc.waitForFinished(5000)); |
959 | QCOMPARE(int(proc.state()), int(QProcess::NotRunning)); |
960 | QCOMPARE(int(proc.error()), int(QProcess::Crashed)); |
961 | } |
962 | |
963 | void tst_QProcess::softExit() |
964 | { |
965 | QProcess proc; |
966 | QCOMPARE(proc.processId(), 0); |
967 | proc.start(command: "testSoftExit/testSoftExit" ); |
968 | |
969 | QVERIFY(proc.waitForStarted(10000)); |
970 | QVERIFY(proc.waitForReadyRead(10000)); |
971 | |
972 | QVERIFY(proc.processId() > 0); |
973 | |
974 | proc.terminate(); |
975 | |
976 | QVERIFY(proc.waitForFinished(10000)); |
977 | QCOMPARE(int(proc.state()), int(QProcess::NotRunning)); |
978 | QCOMPARE(int(proc.error()), int(QProcess::UnknownError)); |
979 | } |
980 | |
981 | class SoftExitProcess : public QProcess |
982 | { |
983 | Q_OBJECT |
984 | public: |
985 | bool waitedForFinished; |
986 | |
987 | SoftExitProcess(int n) : waitedForFinished(false), n(n), killing(false) |
988 | { |
989 | connect(sender: this, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
990 | receiver: this, slot: &SoftExitProcess::finishedSlot); |
991 | |
992 | switch (n) { |
993 | case 0: |
994 | setProcessChannelMode(QProcess::MergedChannels); |
995 | connect(sender: this, signal: &QIODevice::readyRead, receiver: this, slot: &SoftExitProcess::terminateSlot); |
996 | break; |
997 | case 1: |
998 | connect(sender: this, signal: &QProcess::readyReadStandardOutput, |
999 | receiver: this, slot: &SoftExitProcess::terminateSlot); |
1000 | break; |
1001 | case 2: |
1002 | connect(sender: this, signal: &QProcess::readyReadStandardError, |
1003 | receiver: this, slot: &SoftExitProcess::terminateSlot); |
1004 | break; |
1005 | case 3: |
1006 | connect(sender: this, signal: &QProcess::started, |
1007 | receiver: this, slot: &SoftExitProcess::terminateSlot); |
1008 | break; |
1009 | case 4: |
1010 | setProcessChannelMode(QProcess::MergedChannels); |
1011 | connect(sender: this, SIGNAL(channelReadyRead(int)), receiver: this, SLOT(terminateSlot())); |
1012 | break; |
1013 | default: |
1014 | connect(sender: this, signal: &QProcess::stateChanged, |
1015 | receiver: this, slot: &SoftExitProcess::terminateSlot); |
1016 | break; |
1017 | } |
1018 | } |
1019 | |
1020 | void writeAfterStart(const char *buf, int count) |
1021 | { |
1022 | dataToWrite = QByteArray(buf, count); |
1023 | } |
1024 | |
1025 | void start(const QString &program) |
1026 | { |
1027 | QProcess::start(command: program); |
1028 | writePendingData(); |
1029 | } |
1030 | |
1031 | public slots: |
1032 | void terminateSlot() |
1033 | { |
1034 | writePendingData(); // In cases 3 and 5 we haven't written the data yet. |
1035 | if (killing || (n == 5 && state() != Running)) { |
1036 | // Don't try to kill the process before it is running - that can |
1037 | // be hazardous, as the actual child process might not be running |
1038 | // yet. Also, don't kill it "recursively". |
1039 | return; |
1040 | } |
1041 | killing = true; |
1042 | readAll(); |
1043 | terminate(); |
1044 | if ((waitedForFinished = waitForFinished(msecs: 5000)) == false) { |
1045 | kill(); |
1046 | if (state() != NotRunning) |
1047 | waitedForFinished = waitForFinished(msecs: 5000); |
1048 | } |
1049 | } |
1050 | |
1051 | void finishedSlot(int, QProcess::ExitStatus) |
1052 | { |
1053 | waitedForFinished = true; |
1054 | } |
1055 | |
1056 | private: |
1057 | void writePendingData() |
1058 | { |
1059 | if (!dataToWrite.isEmpty()) { |
1060 | write(data: dataToWrite); |
1061 | dataToWrite.clear(); |
1062 | } |
1063 | } |
1064 | |
1065 | private: |
1066 | int n; |
1067 | bool killing; |
1068 | QByteArray dataToWrite; |
1069 | }; |
1070 | |
1071 | void tst_QProcess::softExitInSlots_data() |
1072 | { |
1073 | QTest::addColumn<QString>(name: "appName" ); |
1074 | QTest::addColumn<int>(name: "signalToConnect" ); |
1075 | |
1076 | QByteArray dataTagPrefix("gui app " ); |
1077 | #ifndef QT_NO_WIDGETS |
1078 | for (int i = 0; i < 6; ++i) { |
1079 | QTest::newRow(dataTag: dataTagPrefix + QByteArray::number(i)) |
1080 | << "testGuiProcess/testGuiProcess" << i; |
1081 | } |
1082 | #endif |
1083 | |
1084 | dataTagPrefix = "console app " ; |
1085 | for (int i = 0; i < 6; ++i) { |
1086 | QTest::newRow(dataTag: dataTagPrefix + QByteArray::number(i)) |
1087 | << "testProcessEcho2/testProcessEcho2" << i; |
1088 | } |
1089 | } |
1090 | |
1091 | void tst_QProcess::softExitInSlots() |
1092 | { |
1093 | QFETCH(QString, appName); |
1094 | QFETCH(int, signalToConnect); |
1095 | |
1096 | SoftExitProcess proc(signalToConnect); |
1097 | proc.writeAfterStart(buf: "OLEBOLE" , count: 8); // include the \0 |
1098 | proc.start(program: appName); |
1099 | QTRY_VERIFY_WITH_TIMEOUT(proc.waitedForFinished, 60000); |
1100 | QCOMPARE(proc.state(), QProcess::NotRunning); |
1101 | } |
1102 | |
1103 | void tst_QProcess::mergedChannels() |
1104 | { |
1105 | QProcess process; |
1106 | process.setProcessChannelMode(QProcess::MergedChannels); |
1107 | QCOMPARE(process.processChannelMode(), QProcess::MergedChannels); |
1108 | |
1109 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
1110 | |
1111 | QVERIFY(process.waitForStarted(5000)); |
1112 | |
1113 | for (int i = 0; i < 100; ++i) { |
1114 | QCOMPARE(process.write("abc" ), qlonglong(3)); |
1115 | while (process.bytesAvailable() < 6) |
1116 | QVERIFY(process.waitForReadyRead(5000)); |
1117 | QCOMPARE(process.readAll(), QByteArray("aabbcc" )); |
1118 | } |
1119 | |
1120 | process.closeWriteChannel(); |
1121 | QVERIFY(process.waitForFinished(5000)); |
1122 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1123 | QCOMPARE(process.exitCode(), 0); |
1124 | } |
1125 | |
1126 | void tst_QProcess::forwardedChannels_data() |
1127 | { |
1128 | QTest::addColumn<bool>(name: "detach" ); |
1129 | QTest::addColumn<int>(name: "mode" ); |
1130 | QTest::addColumn<int>(name: "inmode" ); |
1131 | QTest::addColumn<QByteArray>(name: "outdata" ); |
1132 | QTest::addColumn<QByteArray>(name: "errdata" ); |
1133 | |
1134 | QTest::newRow(dataTag: "separate" ) |
1135 | << false |
1136 | << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel) |
1137 | << QByteArray() << QByteArray(); |
1138 | QTest::newRow(dataTag: "forwarded" ) |
1139 | << false |
1140 | << int(QProcess::ForwardedChannels) << int(QProcess::ManagedInputChannel) |
1141 | << QByteArray("forwarded" ) << QByteArray("forwarded" ); |
1142 | QTest::newRow(dataTag: "stdout" ) |
1143 | << false |
1144 | << int(QProcess::ForwardedOutputChannel) << int(QProcess::ManagedInputChannel) |
1145 | << QByteArray("forwarded" ) << QByteArray(); |
1146 | QTest::newRow(dataTag: "stderr" ) |
1147 | << false |
1148 | << int(QProcess::ForwardedErrorChannel) << int(QProcess::ManagedInputChannel) |
1149 | << QByteArray() << QByteArray("forwarded" ); |
1150 | QTest::newRow(dataTag: "fwdinput" ) |
1151 | << false |
1152 | << int(QProcess::ForwardedErrorChannel) << int(QProcess::ForwardedInputChannel) |
1153 | << QByteArray() << QByteArray("input" ); |
1154 | QTest::newRow(dataTag: "detached-default-forwarding" ) |
1155 | << true |
1156 | << int(QProcess::SeparateChannels) << int(QProcess::ManagedInputChannel) |
1157 | << QByteArray("out data" ) << QByteArray("err data" ); |
1158 | } |
1159 | |
1160 | void tst_QProcess::forwardedChannels() |
1161 | { |
1162 | QFETCH(bool, detach); |
1163 | QFETCH(int, mode); |
1164 | QFETCH(int, inmode); |
1165 | QFETCH(QByteArray, outdata); |
1166 | QFETCH(QByteArray, errdata); |
1167 | |
1168 | QProcess process; |
1169 | process.start(program: "testForwarding/testForwarding" , |
1170 | arguments: QStringList() << QString::number(mode) << QString::number(inmode) |
1171 | << QString::number(bool(detach))); |
1172 | QVERIFY(process.waitForStarted(5000)); |
1173 | QCOMPARE(process.write("input" ), 5); |
1174 | process.closeWriteChannel(); |
1175 | QVERIFY(process.waitForFinished(5000)); |
1176 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1177 | QCOMPARE(process.exitCode(), 0); |
1178 | const char *err; |
1179 | switch (process.exitCode()) { |
1180 | case 0: err = "ok" ; break; |
1181 | case 1: err = "processChannelMode is wrong" ; break; |
1182 | case 11: err = "inputChannelMode is wrong" ; break; |
1183 | case 2: err = "failed to start" ; break; |
1184 | case 3: err = "failed to write" ; break; |
1185 | case 4: err = "did not finish" ; break; |
1186 | case 5: err = "unexpected stdout" ; break; |
1187 | case 6: err = "unexpected stderr" ; break; |
1188 | case 12: err = "cannot create temp file" ; break; |
1189 | case 13: err = "startDetached failed" ; break; |
1190 | case 14: err = "waitForDoneFileWritten timed out" ; break; |
1191 | default: err = "unknown exit code" ; break; |
1192 | } |
1193 | QVERIFY2(!process.exitCode(), err); |
1194 | QCOMPARE(process.readAllStandardOutput(), outdata); |
1195 | QCOMPARE(process.readAllStandardError(), errdata); |
1196 | } |
1197 | |
1198 | void tst_QProcess::atEnd() |
1199 | { |
1200 | QProcess process; |
1201 | |
1202 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1203 | process.write(data: "abcdefgh\n" ); |
1204 | |
1205 | while (process.bytesAvailable() < 8) |
1206 | QVERIFY(process.waitForReadyRead(5000)); |
1207 | |
1208 | QTextStream stream(&process); |
1209 | QVERIFY(!stream.atEnd()); |
1210 | QString tmp = stream.readLine(); |
1211 | QVERIFY(stream.atEnd()); |
1212 | QCOMPARE(tmp, QString::fromLatin1("abcdefgh" )); |
1213 | |
1214 | process.write(data: "" , len: 1); |
1215 | QVERIFY(process.waitForFinished(5000)); |
1216 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1217 | QCOMPARE(process.exitCode(), 0); |
1218 | } |
1219 | |
1220 | class TestThread : public QThread |
1221 | { |
1222 | Q_OBJECT |
1223 | public: |
1224 | inline int code() |
1225 | { |
1226 | return exitCode; |
1227 | } |
1228 | |
1229 | protected: |
1230 | inline void run() |
1231 | { |
1232 | exitCode = 90210; |
1233 | |
1234 | QProcess process; |
1235 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
1236 | receiver: this, slot: &TestThread::catchExitCode, type: Qt::DirectConnection); |
1237 | |
1238 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1239 | |
1240 | QCOMPARE(process.write("abc\0" , 4), qint64(4)); |
1241 | exitCode = exec(); |
1242 | } |
1243 | |
1244 | protected slots: |
1245 | inline void catchExitCode(int exitCode) |
1246 | { |
1247 | this->exitCode = exitCode; |
1248 | exit(retcode: exitCode); |
1249 | } |
1250 | |
1251 | private: |
1252 | int exitCode; |
1253 | }; |
1254 | |
1255 | void tst_QProcess::processInAThread() |
1256 | { |
1257 | for (int i = 0; i < 10; ++i) { |
1258 | TestThread thread; |
1259 | thread.start(); |
1260 | QVERIFY(thread.wait(10000)); |
1261 | QCOMPARE(thread.code(), 0); |
1262 | } |
1263 | } |
1264 | |
1265 | void tst_QProcess::processesInMultipleThreads() |
1266 | { |
1267 | if (EmulationDetector::isRunningArmOnX86()) |
1268 | QSKIP("Flakily hangs in QEMU. QTBUG-67760" ); |
1269 | for (int i = 0; i < 10; ++i) { |
1270 | // run from 1 to 10 threads, but run at least some tests |
1271 | // with more threads than the ideal |
1272 | int threadCount = i; |
1273 | if (i > 7) |
1274 | threadCount = qMax(a: threadCount, b: QThread::idealThreadCount() + 2); |
1275 | |
1276 | QVector<TestThread *> threads(threadCount); |
1277 | auto cleanup = qScopeGuard(f: [&threads]() { qDeleteAll(c: threads); }); |
1278 | for (int j = 0; j < threadCount; ++j) |
1279 | threads[j] = new TestThread; |
1280 | for (int j = 0; j < threadCount; ++j) |
1281 | threads[j]->start(); |
1282 | for (int j = 0; j < threadCount; ++j) |
1283 | QVERIFY(threads[j]->wait(10000)); |
1284 | for (int j = 0; j < threadCount; ++j) |
1285 | QCOMPARE(threads[j]->code(), 0); |
1286 | } |
1287 | } |
1288 | |
1289 | void tst_QProcess::waitForFinishedWithTimeout() |
1290 | { |
1291 | QProcess process; |
1292 | |
1293 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1294 | |
1295 | QVERIFY(process.waitForStarted(5000)); |
1296 | QVERIFY(!process.waitForFinished(1)); |
1297 | |
1298 | process.write(data: "" , len: 1); |
1299 | |
1300 | QVERIFY(process.waitForFinished()); |
1301 | } |
1302 | |
1303 | void tst_QProcess::waitForReadyReadInAReadyReadSlot() |
1304 | { |
1305 | QProcess process; |
1306 | connect(sender: &process, signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::waitForReadyReadInAReadyReadSlotSlot); |
1307 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
1308 | receiver: this, slot: &tst_QProcess::exitLoopSlot); |
1309 | bytesAvailable = 0; |
1310 | |
1311 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1312 | QVERIFY(process.waitForStarted(5000)); |
1313 | |
1314 | QSignalSpy spy(&process, &QProcess::readyRead); |
1315 | QVERIFY(spy.isValid()); |
1316 | process.write(data: "foo" ); |
1317 | QTestEventLoop::instance().enterLoop(secs: 30); |
1318 | QVERIFY(!QTestEventLoop::instance().timeout()); |
1319 | |
1320 | QCOMPARE(spy.count(), 1); |
1321 | |
1322 | process.disconnect(); |
1323 | QVERIFY(process.waitForFinished(5000)); |
1324 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1325 | QCOMPARE(process.exitCode(), 0); |
1326 | QVERIFY(process.bytesAvailable() > bytesAvailable); |
1327 | } |
1328 | |
1329 | void tst_QProcess::waitForReadyReadInAReadyReadSlotSlot() |
1330 | { |
1331 | QProcess *process = qobject_cast<QProcess *>(object: sender()); |
1332 | QVERIFY(process); |
1333 | bytesAvailable = process->bytesAvailable(); |
1334 | process->write(data: "bar" , len: 4); |
1335 | QVERIFY(process->waitForReadyRead(5000)); |
1336 | QTestEventLoop::instance().exitLoop(); |
1337 | } |
1338 | |
1339 | void tst_QProcess::waitForBytesWrittenInABytesWrittenSlot() |
1340 | { |
1341 | QProcess process; |
1342 | connect(sender: &process, signal: &QIODevice::bytesWritten, receiver: this, slot: &tst_QProcess::waitForBytesWrittenInABytesWrittenSlotSlot); |
1343 | bytesAvailable = 0; |
1344 | |
1345 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1346 | QVERIFY(process.waitForStarted(5000)); |
1347 | |
1348 | QSignalSpy spy(&process, &QProcess::bytesWritten); |
1349 | QVERIFY(spy.isValid()); |
1350 | process.write(data: "f" ); |
1351 | QTestEventLoop::instance().enterLoop(secs: 30); |
1352 | QVERIFY(!QTestEventLoop::instance().timeout()); |
1353 | |
1354 | QCOMPARE(spy.count(), 1); |
1355 | process.write(data: "" , len: 1); |
1356 | process.disconnect(); |
1357 | QVERIFY(process.waitForFinished()); |
1358 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1359 | QCOMPARE(process.exitCode(), 0); |
1360 | } |
1361 | |
1362 | void tst_QProcess::waitForBytesWrittenInABytesWrittenSlotSlot() |
1363 | { |
1364 | QProcess *process = qobject_cast<QProcess *>(object: sender()); |
1365 | QVERIFY(process); |
1366 | process->write(data: "b" ); |
1367 | QVERIFY(process->waitForBytesWritten(5000)); |
1368 | QTestEventLoop::instance().exitLoop(); |
1369 | } |
1370 | |
1371 | void tst_QProcess::spaceArgsTest_data() |
1372 | { |
1373 | QTest::addColumn<QStringList>(name: "args" ); |
1374 | QTest::addColumn<QString>(name: "stringArgs" ); |
1375 | |
1376 | // arg1 | arg2 |
1377 | QTest::newRow(dataTag: "arg1 arg2" ) << (QStringList() << QString::fromLatin1(str: "arg1" ) << QString::fromLatin1(str: "arg2" )) |
1378 | << QString::fromLatin1(str: "arg1 arg2" ); |
1379 | // "arg1" | ar "g2 |
1380 | QTest::newRow(dataTag: "\"\"\"\"arg1\"\"\"\" \"ar \"\"\"g2\"" ) << (QStringList() << QString::fromLatin1(str: "\"arg1\"" ) << QString::fromLatin1(str: "ar \"g2" )) |
1381 | << QString::fromLatin1(str: "\"\"\"\"arg1\"\"\"\" \"ar \"\"\"g2\"" ); |
1382 | // ar g1 | a rg 2 |
1383 | QTest::newRow(dataTag: "\"ar g1\" \"a rg 2\"" ) << (QStringList() << QString::fromLatin1(str: "ar g1" ) << QString::fromLatin1(str: "a rg 2" )) |
1384 | << QString::fromLatin1(str: "\"ar g1\" \"a rg 2\"" ); |
1385 | // -lar g1 | -l"ar g2" |
1386 | QTest::newRow(dataTag: "\"-lar g1\" \"-l\"\"\"ar g2\"\"\"\"" ) << (QStringList() << QString::fromLatin1(str: "-lar g1" ) << QString::fromLatin1(str: "-l\"ar g2\"" )) |
1387 | << QString::fromLatin1(str: "\"-lar g1\" \"-l\"\"\"ar g2\"\"\"\"" ); |
1388 | // ar"g1 |
1389 | QTest::newRow(dataTag: "ar\"\"\"\"g1" ) << (QStringList() << QString::fromLatin1(str: "ar\"g1" )) |
1390 | << QString::fromLatin1(str: "ar\"\"\"\"g1" ); |
1391 | // ar/g1 |
1392 | QTest::newRow(dataTag: "ar\\g1" ) << (QStringList() << QString::fromLatin1(str: "ar\\g1" )) |
1393 | << QString::fromLatin1(str: "ar\\g1" ); |
1394 | // ar\g"1 |
1395 | QTest::newRow(dataTag: "ar\\g\"\"\"\"1" ) << (QStringList() << QString::fromLatin1(str: "ar\\g\"1" )) |
1396 | << QString::fromLatin1(str: "ar\\g\"\"\"\"1" ); |
1397 | // arg\"1 |
1398 | QTest::newRow(dataTag: "arg\\\"\"\"1" ) << (QStringList() << QString::fromLatin1(str: "arg\\\"1" )) |
1399 | << QString::fromLatin1(str: "arg\\\"\"\"1" ); |
1400 | // """" |
1401 | QTest::newRow(dataTag: "\"\"\"\"\"\"\"\"\"\"\"\"" ) << (QStringList() << QString::fromLatin1(str: "\"\"\"\"" )) |
1402 | << QString::fromLatin1(str: "\"\"\"\"\"\"\"\"\"\"\"\"" ); |
1403 | // """" | "" "" |
1404 | QTest::newRow(dataTag: "\"\"\"\"\"\"\"\"\"\"\"\" \"\"\"\"\"\"\" \"\"\"\"\"\"\"" ) << (QStringList() << QString::fromLatin1(str: "\"\"\"\"" ) << QString::fromLatin1(str: "\"\" \"\"" )) |
1405 | << QString::fromLatin1(str: "\"\"\"\"\"\"\"\"\"\"\"\" \"\"\"\"\"\"\" \"\"\"\"\"\"\"" ); |
1406 | // "" "" |
1407 | QTest::newRow(dataTag: "\"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" (bogus double quotes)" ) << (QStringList() << QString::fromLatin1(str: "\"\" \"\"" )) |
1408 | << QString::fromLatin1(str: "\"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\"" ); |
1409 | // "" "" |
1410 | QTest::newRow(dataTag: " \"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" (bogus double quotes)" ) << (QStringList() << QString::fromLatin1(str: "\"\" \"\"" )) |
1411 | << QString::fromLatin1(str: " \"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" " ); |
1412 | } |
1413 | |
1414 | static QByteArray startFailMessage(const QString &program, const QProcess &process) |
1415 | { |
1416 | QByteArray result = "Process '" ; |
1417 | result += program.toLocal8Bit(); |
1418 | result += "' failed to start: " ; |
1419 | result += process.errorString().toLocal8Bit(); |
1420 | return result; |
1421 | } |
1422 | |
1423 | void tst_QProcess::spaceArgsTest() |
1424 | { |
1425 | QFETCH(QStringList, args); |
1426 | QFETCH(QString, stringArgs); |
1427 | |
1428 | QStringList programs; |
1429 | programs << QString::fromLatin1(str: "testProcessSpacesArgs/nospace" ) |
1430 | << QString::fromLatin1(str: "testProcessSpacesArgs/one space" ) |
1431 | << QString::fromLatin1(str: "testProcessSpacesArgs/two space s" ); |
1432 | |
1433 | QProcess process; |
1434 | |
1435 | for (int i = 0; i < programs.size(); ++i) { |
1436 | QString program = programs.at(i); |
1437 | process.start(program, arguments: args); |
1438 | |
1439 | QByteArray errorMessage; |
1440 | bool started = process.waitForStarted(); |
1441 | if (!started) |
1442 | errorMessage = startFailMessage(program, process); |
1443 | QVERIFY2(started, errorMessage.constData()); |
1444 | QVERIFY(process.waitForFinished()); |
1445 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1446 | QCOMPARE(process.exitCode(), 0); |
1447 | |
1448 | QStringList actual = QString::fromLatin1(str: process.readAll()).split(sep: "|" ); |
1449 | QVERIFY(!actual.isEmpty()); |
1450 | // not interested in the program name, it might be different. |
1451 | actual.removeFirst(); |
1452 | |
1453 | QCOMPARE(actual, args); |
1454 | |
1455 | if (program.contains(c: QLatin1Char(' '))) |
1456 | program = QLatin1Char('"') + program + QLatin1Char('"'); |
1457 | |
1458 | if (!stringArgs.isEmpty()) |
1459 | program += QLatin1Char(' ') + stringArgs; |
1460 | |
1461 | errorMessage.clear(); |
1462 | process.start(command: program); |
1463 | started = process.waitForStarted(msecs: 5000); |
1464 | if (!started) |
1465 | errorMessage = startFailMessage(program, process); |
1466 | |
1467 | QVERIFY2(started, errorMessage.constData()); |
1468 | QVERIFY(process.waitForFinished(5000)); |
1469 | |
1470 | actual = QString::fromLatin1(str: process.readAll()).split(sep: "|" ); |
1471 | QVERIFY(!actual.isEmpty()); |
1472 | // not interested in the program name, it might be different. |
1473 | actual.removeFirst(); |
1474 | |
1475 | QCOMPARE(actual, args); |
1476 | } |
1477 | } |
1478 | |
1479 | #if defined(Q_OS_WIN) |
1480 | |
1481 | void tst_QProcess::nativeArguments() |
1482 | { |
1483 | QProcess proc; |
1484 | |
1485 | // This doesn't actually need special quoting, so it is pointless to use |
1486 | // native arguments here, but that's not the point of this test. |
1487 | proc.setNativeArguments("hello kitty, \"*\"!" ); |
1488 | |
1489 | proc.start(QString::fromLatin1("testProcessSpacesArgs/nospace" ), QStringList()); |
1490 | |
1491 | QVERIFY2(proc.waitForStarted(), qPrintable(proc.errorString())); |
1492 | QVERIFY(proc.waitForFinished()); |
1493 | QCOMPARE(proc.exitStatus(), QProcess::NormalExit); |
1494 | QCOMPARE(proc.exitCode(), 0); |
1495 | |
1496 | QStringList actual = QString::fromLatin1(proc.readAll()).split(QLatin1Char('|')); |
1497 | QVERIFY(!actual.isEmpty()); |
1498 | // not interested in the program name, it might be different. |
1499 | actual.removeFirst(); |
1500 | QStringList expected; |
1501 | expected << "hello" << "kitty," << "*!" ; |
1502 | QCOMPARE(actual, expected); |
1503 | } |
1504 | |
1505 | void tst_QProcess::createProcessArgumentsModifier() |
1506 | { |
1507 | int calls = 0; |
1508 | const QString reversedCommand = "lamroNssecorPtset/lamroNssecorPtset" ; |
1509 | QProcess process; |
1510 | process.setCreateProcessArgumentsModifier([&calls] (QProcess::CreateProcessArguments *args) |
1511 | { |
1512 | calls++; |
1513 | std::reverse(args->arguments, args->arguments + wcslen(args->arguments) - 1); |
1514 | }); |
1515 | process.start(reversedCommand); |
1516 | QVERIFY2(process.waitForStarted(), qUtf8Printable(process.errorString())); |
1517 | QVERIFY(process.waitForFinished()); |
1518 | QCOMPARE(calls, 1); |
1519 | |
1520 | process.setCreateProcessArgumentsModifier(QProcess::CreateProcessArgumentModifier()); |
1521 | QVERIFY(!process.waitForStarted()); |
1522 | QCOMPARE(calls, 1); |
1523 | } |
1524 | #endif // Q_OS_WIN |
1525 | |
1526 | void tst_QProcess::exitCodeTest() |
1527 | { |
1528 | for (int i = 0; i < 255; ++i) { |
1529 | QProcess process; |
1530 | process.start(command: "testExitCodes/testExitCodes " + QString::number(i)); |
1531 | QVERIFY(process.waitForFinished(5000)); |
1532 | QCOMPARE(process.exitCode(), i); |
1533 | QCOMPARE(process.error(), QProcess::UnknownError); |
1534 | } |
1535 | } |
1536 | |
1537 | void tst_QProcess::failToStart() |
1538 | { |
1539 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
1540 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
1541 | qRegisterMetaType<QProcess::ProcessState>(typeName: "QProcess::ProcessState" ); |
1542 | |
1543 | QProcess process; |
1544 | QSignalSpy stateSpy(&process, &QProcess::stateChanged); |
1545 | QSignalSpy errorSpy(&process, &QProcess::errorOccurred); |
1546 | QSignalSpy finishedSpy(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
1547 | QVERIFY(stateSpy.isValid()); |
1548 | QVERIFY(errorSpy.isValid()); |
1549 | QVERIFY(finishedSpy.isValid()); |
1550 | |
1551 | #if QT_DEPRECATED_SINCE(5, 6) |
1552 | QSignalSpy errorSpy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
1553 | QVERIFY(errorSpy2.isValid()); |
1554 | #endif |
1555 | #if QT_DEPRECATED_SINCE(5, 13) |
1556 | QSignalSpy finishedSpy2(&process, static_cast<QProcessFinishedSignal1>(&QProcess::finished)); |
1557 | QVERIFY(finishedSpy2.isValid()); |
1558 | #endif |
1559 | |
1560 | // OS X and HP-UX have a really low default process limit (~100), so spawning |
1561 | // to many processes here will cause test failures later on. |
1562 | #if defined Q_OS_HPUX |
1563 | const int attempts = 15; |
1564 | #elif defined Q_OS_MAC |
1565 | const int attempts = 15; |
1566 | #else |
1567 | const int attempts = 50; |
1568 | #endif |
1569 | |
1570 | for (int j = 0; j < 8; ++j) { |
1571 | for (int i = 0; i < attempts; ++i) { |
1572 | QCOMPARE(errorSpy.count(), j * attempts + i); |
1573 | #if QT_DEPRECATED_SINCE(5, 6) |
1574 | QCOMPARE(errorSpy2.count(), j * attempts + i); |
1575 | #endif |
1576 | process.start(command: "/blurp" ); |
1577 | |
1578 | switch (j) { |
1579 | case 0: |
1580 | case 1: |
1581 | QVERIFY(!process.waitForStarted()); |
1582 | break; |
1583 | case 2: |
1584 | case 3: |
1585 | QVERIFY(!process.waitForFinished()); |
1586 | break; |
1587 | case 4: |
1588 | case 5: |
1589 | QVERIFY(!process.waitForReadyRead()); |
1590 | break; |
1591 | case 6: |
1592 | case 7: |
1593 | default: |
1594 | QVERIFY(!process.waitForBytesWritten()); |
1595 | break; |
1596 | } |
1597 | |
1598 | QCOMPARE(process.error(), QProcess::FailedToStart); |
1599 | QCOMPARE(errorSpy.count(), j * attempts + i + 1); |
1600 | QCOMPARE(finishedSpy.count(), 0); |
1601 | #if QT_DEPRECATED_SINCE(5, 6) |
1602 | QCOMPARE(errorSpy2.count(), j * attempts + i + 1); |
1603 | #endif |
1604 | #if QT_DEPRECATED_SINCE(5, 13) |
1605 | QCOMPARE(finishedSpy2.count(), 0); |
1606 | #endif |
1607 | |
1608 | int it = j * attempts + i + 1; |
1609 | |
1610 | QCOMPARE(stateSpy.count(), it * 2); |
1611 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(stateSpy.at(it * 2 - 2).at(0)), QProcess::Starting); |
1612 | QCOMPARE(qvariant_cast<QProcess::ProcessState>(stateSpy.at(it * 2 - 1).at(0)), QProcess::NotRunning); |
1613 | } |
1614 | } |
1615 | } |
1616 | |
1617 | void tst_QProcess::failToStartWithWait() |
1618 | { |
1619 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
1620 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
1621 | |
1622 | QProcess process; |
1623 | QEventLoop loop; |
1624 | QSignalSpy errorSpy(&process, &QProcess::errorOccurred); |
1625 | QSignalSpy finishedSpy(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
1626 | QVERIFY(errorSpy.isValid()); |
1627 | QVERIFY(finishedSpy.isValid()); |
1628 | |
1629 | #if QT_DEPRECATED_SINCE(5, 6) |
1630 | QSignalSpy errorSpy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
1631 | QVERIFY(errorSpy2.isValid()); |
1632 | #endif |
1633 | #if QT_DEPRECATED_SINCE(5, 13) |
1634 | QSignalSpy finishedSpy2(&process, static_cast<QProcessFinishedSignal1>(&QProcess::finished)); |
1635 | QVERIFY(finishedSpy2.isValid()); |
1636 | #endif |
1637 | |
1638 | for (int i = 0; i < 50; ++i) { |
1639 | process.start(program: "/blurp" , arguments: QStringList() << "-v" << "-debug" ); |
1640 | process.waitForStarted(); |
1641 | |
1642 | QCOMPARE(process.error(), QProcess::FailedToStart); |
1643 | QCOMPARE(errorSpy.count(), i + 1); |
1644 | QCOMPARE(finishedSpy.count(), 0); |
1645 | #if QT_DEPRECATED_SINCE(5, 6) |
1646 | QCOMPARE(errorSpy2.count(), i + 1); |
1647 | #endif |
1648 | #if QT_DEPRECATED_SINCE(5, 13) |
1649 | QCOMPARE(finishedSpy2.count(), 0); |
1650 | #endif |
1651 | |
1652 | } |
1653 | } |
1654 | |
1655 | void tst_QProcess::failToStartWithEventLoop() |
1656 | { |
1657 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
1658 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
1659 | |
1660 | QProcess process; |
1661 | QEventLoop loop; |
1662 | QSignalSpy errorSpy(&process, &QProcess::errorOccurred); |
1663 | QSignalSpy finishedSpy(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
1664 | QVERIFY(errorSpy.isValid()); |
1665 | QVERIFY(finishedSpy.isValid()); |
1666 | |
1667 | #if QT_DEPRECATED_SINCE(5, 6) |
1668 | QSignalSpy errorSpy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
1669 | QVERIFY(errorSpy2.isValid()); |
1670 | #endif |
1671 | #if QT_DEPRECATED_SINCE(5, 13) |
1672 | QSignalSpy finishedSpy2(&process, static_cast<QProcessFinishedSignal1>(&QProcess::finished)); |
1673 | QVERIFY(finishedSpy2.isValid()); |
1674 | #endif |
1675 | |
1676 | // The error signal may be emitted before start() returns |
1677 | connect(sender: &process, signal: &QProcess::errorOccurred, receiver: &loop, slot: &QEventLoop::quit, type: Qt::QueuedConnection); |
1678 | |
1679 | |
1680 | for (int i = 0; i < 50; ++i) { |
1681 | process.start(program: "/blurp" , arguments: QStringList() << "-v" << "-debug" ); |
1682 | |
1683 | loop.exec(); |
1684 | |
1685 | QCOMPARE(process.error(), QProcess::FailedToStart); |
1686 | QCOMPARE(errorSpy.count(), i + 1); |
1687 | QCOMPARE(finishedSpy.count(), 0); |
1688 | #if QT_DEPRECATED_SINCE(5, 6) |
1689 | QCOMPARE(errorSpy2.count(), i + 1); |
1690 | #endif |
1691 | #if QT_DEPRECATED_SINCE(5, 13) |
1692 | QCOMPARE(finishedSpy2.count(), 0); |
1693 | #endif |
1694 | } |
1695 | } |
1696 | |
1697 | void tst_QProcess::failToStartEmptyArgs_data() |
1698 | { |
1699 | QTest::addColumn<int>(name: "startOverload" ); |
1700 | QTest::newRow(dataTag: "start(QString, QStringList, OpenMode)" ) << 0; |
1701 | QTest::newRow(dataTag: "start(QString, OpenMode)" ) << 1; |
1702 | QTest::newRow(dataTag: "start(OpenMode)" ) << 2; |
1703 | } |
1704 | |
1705 | void tst_QProcess::failToStartEmptyArgs() |
1706 | { |
1707 | QFETCH(int, startOverload); |
1708 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
1709 | |
1710 | QProcess process; |
1711 | QSignalSpy errorSpy(&process, static_cast<QProcessErrorSignal>(&QProcess::errorOccurred)); |
1712 | QVERIFY(errorSpy.isValid()); |
1713 | #if QT_DEPRECATED_SINCE(5, 6) |
1714 | QSignalSpy errorSpy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
1715 | QVERIFY(errorSpy2.isValid()); |
1716 | #endif |
1717 | |
1718 | switch (startOverload) { |
1719 | case 0: |
1720 | process.start(program: QString(), arguments: QStringList(), mode: QIODevice::ReadWrite); |
1721 | break; |
1722 | case 1: |
1723 | process.start(command: QString(), mode: QIODevice::ReadWrite); |
1724 | break; |
1725 | case 2: |
1726 | process.start(mode: QIODevice::ReadWrite); |
1727 | break; |
1728 | default: |
1729 | QFAIL("Unhandled QProcess::start overload." ); |
1730 | }; |
1731 | |
1732 | QVERIFY(!process.waitForStarted()); |
1733 | QCOMPARE(errorSpy.count(), 1); |
1734 | #if QT_DEPRECATED_SINCE(5, 6) |
1735 | QCOMPARE(errorSpy2.count(), 1); |
1736 | #endif |
1737 | QCOMPARE(process.error(), QProcess::FailedToStart); |
1738 | } |
1739 | |
1740 | void tst_QProcess::removeFileWhileProcessIsRunning() |
1741 | { |
1742 | QFile file(m_temporaryDir.path() + QLatin1String("/removeFile.txt" )); |
1743 | QVERIFY(file.open(QFile::WriteOnly)); |
1744 | |
1745 | QProcess process; |
1746 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1747 | |
1748 | QVERIFY(process.waitForStarted(5000)); |
1749 | |
1750 | QVERIFY(file.remove()); |
1751 | |
1752 | process.write(data: "" , len: 1); |
1753 | QVERIFY(process.waitForFinished(5000)); |
1754 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1755 | QCOMPARE(process.exitCode(), 0); |
1756 | } |
1757 | |
1758 | void tst_QProcess::setEnvironment_data() |
1759 | { |
1760 | QTest::addColumn<QString>(name: "name" ); |
1761 | QTest::addColumn<QString>(name: "value" ); |
1762 | |
1763 | QTest::newRow(dataTag: "setting-empty" ) << "tst_QProcess" << "" ; |
1764 | QTest::newRow(dataTag: "setting" ) << "tst_QProcess" << "value" ; |
1765 | |
1766 | #ifdef Q_OS_WIN |
1767 | QTest::newRow("unsetting" ) << "PROMPT" << QString(); |
1768 | QTest::newRow("overriding" ) << "PROMPT" << "value" ; |
1769 | #else |
1770 | QTest::newRow(dataTag: "unsetting" ) << "PATH" << QString(); |
1771 | QTest::newRow(dataTag: "overriding" ) << "PATH" << "value" ; |
1772 | #endif |
1773 | } |
1774 | |
1775 | void tst_QProcess::setEnvironment() |
1776 | { |
1777 | // make sure our environment variables are correct |
1778 | QVERIFY(qgetenv("tst_QProcess" ).isEmpty()); |
1779 | QVERIFY(!qgetenv("PATH" ).isEmpty()); |
1780 | #ifdef Q_OS_WIN |
1781 | QVERIFY(!qgetenv("PROMPT" ).isEmpty()); |
1782 | #endif |
1783 | |
1784 | QFETCH(QString, name); |
1785 | QFETCH(QString, value); |
1786 | QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment" ; |
1787 | |
1788 | { |
1789 | QProcess process; |
1790 | QStringList environment = QProcess::systemEnvironment(); |
1791 | if (value.isNull()) { |
1792 | int pos; |
1793 | QRegExp rx(name + "=.*" ); |
1794 | #ifdef Q_OS_WIN |
1795 | rx.setCaseSensitivity(Qt::CaseInsensitive); |
1796 | #endif |
1797 | while ((pos = environment.indexOf(rx)) != -1) |
1798 | environment.removeAt(i: pos); |
1799 | } else { |
1800 | environment.append(t: name + '=' + value); |
1801 | } |
1802 | process.setEnvironment(environment); |
1803 | process.start(program: executable, arguments: QStringList() << name); |
1804 | |
1805 | QVERIFY(process.waitForFinished()); |
1806 | if (value.isNull()) |
1807 | QCOMPARE(process.exitCode(), 1); |
1808 | else if (!value.isEmpty()) |
1809 | QCOMPARE(process.exitCode(), 0); |
1810 | |
1811 | QCOMPARE(process.readAll(), value.toLocal8Bit()); |
1812 | } |
1813 | |
1814 | // re-do the test but set the environment twice, to make sure |
1815 | // that the latter addition overrides |
1816 | // this test doesn't make sense in unsetting |
1817 | if (!value.isNull()) { |
1818 | QProcess process; |
1819 | QStringList environment = QProcess::systemEnvironment(); |
1820 | environment.prepend(t: name + "=This is not the right value" ); |
1821 | environment.append(t: name + '=' + value); |
1822 | process.setEnvironment(environment); |
1823 | process.start(program: executable, arguments: QStringList() << name); |
1824 | |
1825 | QVERIFY(process.waitForFinished()); |
1826 | if (!value.isEmpty()) |
1827 | QCOMPARE(process.exitCode(), 0); |
1828 | |
1829 | QCOMPARE(process.readAll(), value.toLocal8Bit()); |
1830 | } |
1831 | } |
1832 | |
1833 | void tst_QProcess::setProcessEnvironment_data() |
1834 | { |
1835 | setEnvironment_data(); |
1836 | } |
1837 | |
1838 | void tst_QProcess::setProcessEnvironment() |
1839 | { |
1840 | // make sure our environment variables are correct |
1841 | QVERIFY(qgetenv("tst_QProcess" ).isEmpty()); |
1842 | QVERIFY(!qgetenv("PATH" ).isEmpty()); |
1843 | #ifdef Q_OS_WIN |
1844 | QVERIFY(!qgetenv("PROMPT" ).isEmpty()); |
1845 | #endif |
1846 | |
1847 | QFETCH(QString, name); |
1848 | QFETCH(QString, value); |
1849 | QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment" ; |
1850 | |
1851 | { |
1852 | QProcess process; |
1853 | QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); |
1854 | if (value.isNull()) |
1855 | environment.remove(name); |
1856 | else |
1857 | environment.insert(name, value); |
1858 | process.setProcessEnvironment(environment); |
1859 | process.start(program: executable, arguments: QStringList() << name); |
1860 | |
1861 | QVERIFY(process.waitForFinished()); |
1862 | if (value.isNull()) |
1863 | QCOMPARE(process.exitCode(), 1); |
1864 | else if (!value.isEmpty()) |
1865 | QCOMPARE(process.exitCode(), 0); |
1866 | |
1867 | QCOMPARE(process.readAll(), value.toLocal8Bit()); |
1868 | } |
1869 | } |
1870 | |
1871 | void tst_QProcess::environmentIsSorted() |
1872 | { |
1873 | QProcessEnvironment env; |
1874 | env.insert(name: QLatin1String("a" ), value: QLatin1String("foo_a" )); |
1875 | env.insert(name: QLatin1String("B" ), value: QLatin1String("foo_B" )); |
1876 | env.insert(name: QLatin1String("c" ), value: QLatin1String("foo_c" )); |
1877 | env.insert(name: QLatin1String("D" ), value: QLatin1String("foo_D" )); |
1878 | env.insert(name: QLatin1String("e" ), value: QLatin1String("foo_e" )); |
1879 | env.insert(name: QLatin1String("F" ), value: QLatin1String("foo_F" )); |
1880 | env.insert(name: QLatin1String("Path" ), value: QLatin1String("foo_Path" )); |
1881 | env.insert(name: QLatin1String("SystemRoot" ), value: QLatin1String("foo_SystemRoot" )); |
1882 | |
1883 | const QStringList envlist = env.toStringList(); |
1884 | |
1885 | #ifdef Q_OS_WIN32 |
1886 | // The environment block passed to CreateProcess "[Requires that] All strings in the |
1887 | // environment block must be sorted alphabetically by name. The sort is case-insensitive, |
1888 | // Unicode order, without regard to locale." |
1889 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682009(v=vs.85).aspx |
1890 | // So on Windows we sort that way. |
1891 | const QStringList expected = { QLatin1String("a=foo_a" ), |
1892 | QLatin1String("B=foo_B" ), |
1893 | QLatin1String("c=foo_c" ), |
1894 | QLatin1String("D=foo_D" ), |
1895 | QLatin1String("e=foo_e" ), |
1896 | QLatin1String("F=foo_F" ), |
1897 | QLatin1String("Path=foo_Path" ), |
1898 | QLatin1String("SystemRoot=foo_SystemRoot" ) }; |
1899 | #else |
1900 | const QStringList expected = { QLatin1String("B=foo_B" ), |
1901 | QLatin1String("D=foo_D" ), |
1902 | QLatin1String("F=foo_F" ), |
1903 | QLatin1String("Path=foo_Path" ), |
1904 | QLatin1String("SystemRoot=foo_SystemRoot" ), |
1905 | QLatin1String("a=foo_a" ), |
1906 | QLatin1String("c=foo_c" ), |
1907 | QLatin1String("e=foo_e" ) }; |
1908 | #endif |
1909 | QCOMPARE(envlist, expected); |
1910 | } |
1911 | |
1912 | void tst_QProcess::systemEnvironment() |
1913 | { |
1914 | QVERIFY(!QProcess::systemEnvironment().isEmpty()); |
1915 | QVERIFY(!QProcessEnvironment::systemEnvironment().isEmpty()); |
1916 | |
1917 | QVERIFY(QProcessEnvironment::systemEnvironment().contains("PATH" )); |
1918 | QVERIFY(!QProcess::systemEnvironment().filter(QRegExp("^PATH=" , Qt::CaseInsensitive)).isEmpty()); |
1919 | } |
1920 | |
1921 | void tst_QProcess::spaceInName() |
1922 | { |
1923 | QProcess process; |
1924 | process.start(program: "test Space In Name/testSpaceInName" , arguments: QStringList()); |
1925 | QVERIFY(process.waitForStarted()); |
1926 | process.write(data: "" , len: 1); |
1927 | QVERIFY(process.waitForFinished()); |
1928 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
1929 | QCOMPARE(process.exitCode(), 0); |
1930 | } |
1931 | |
1932 | void tst_QProcess::lockupsInStartDetached() |
1933 | { |
1934 | // Check that QProcess doesn't cause a lock up at this program's |
1935 | // exit if a thread was started and we tried to run a program that |
1936 | // doesn't exist. Before Qt 4.2, this used to lock up on Unix due |
1937 | // to calling ::exit instead of ::_exit if execve failed. |
1938 | |
1939 | QObject *dummy = new QObject(this); |
1940 | QHostInfo::lookupHost(name: QString("something.invalid" ), receiver: dummy, SLOT(deleteLater())); |
1941 | QProcess::execute(command: "yjhbrty" ); |
1942 | QProcess::startDetached(command: "yjhbrty" ); |
1943 | } |
1944 | |
1945 | void tst_QProcess::atEnd2() |
1946 | { |
1947 | QProcess process; |
1948 | |
1949 | process.start(command: "testProcessEcho/testProcessEcho" ); |
1950 | process.write(data: "Foo\nBar\nBaz\nBodukon\nHadukan\nTorwukan\nend\n" ); |
1951 | process.putChar(c: '\0'); |
1952 | QVERIFY(process.waitForFinished()); |
1953 | QList<QByteArray> lines; |
1954 | while (!process.atEnd()) { |
1955 | lines << process.readLine(); |
1956 | } |
1957 | QCOMPARE(lines.size(), 7); |
1958 | } |
1959 | |
1960 | void tst_QProcess::waitForReadyReadForNonexistantProcess() |
1961 | { |
1962 | // Start a program that doesn't exist, process events and then try to waitForReadyRead |
1963 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
1964 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
1965 | |
1966 | QProcess process; |
1967 | QSignalSpy errorSpy(&process, &QProcess::errorOccurred); |
1968 | QSignalSpy finishedSpy(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
1969 | QVERIFY(errorSpy.isValid()); |
1970 | QVERIFY(finishedSpy.isValid()); |
1971 | |
1972 | #if QT_DEPRECATED_SINCE(5, 6) |
1973 | QSignalSpy errorSpy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
1974 | QVERIFY(errorSpy2.isValid()); |
1975 | #endif |
1976 | #if QT_DEPRECATED_SINCE(5, 13) |
1977 | QSignalSpy finishedSpy1(&process, static_cast<QProcessFinishedSignal1>(&QProcess::finished)); |
1978 | QVERIFY(finishedSpy1.isValid()); |
1979 | #endif |
1980 | |
1981 | QVERIFY(!process.waitForReadyRead()); // used to crash |
1982 | process.start(command: "doesntexist" ); |
1983 | QVERIFY(!process.waitForReadyRead()); |
1984 | QCOMPARE(errorSpy.count(), 1); |
1985 | QCOMPARE(errorSpy.at(0).at(0).toInt(), 0); |
1986 | QCOMPARE(finishedSpy.count(), 0); |
1987 | #if QT_DEPRECATED_SINCE(5, 6) |
1988 | QCOMPARE(errorSpy2.count(), 1); |
1989 | QCOMPARE(errorSpy2.at(0).at(0).toInt(), 0); |
1990 | #endif |
1991 | #if QT_DEPRECATED_SINCE(5, 13) |
1992 | QCOMPARE(finishedSpy1.count(), 0); |
1993 | #endif |
1994 | } |
1995 | |
1996 | void tst_QProcess::setStandardInputFile() |
1997 | { |
1998 | static const char data[] = "A bunch\1of\2data\3\4\5\6\7..." ; |
1999 | QProcess process; |
2000 | QFile file(m_temporaryDir.path() + QLatin1String("/data-sif" )); |
2001 | |
2002 | QVERIFY(file.open(QIODevice::WriteOnly)); |
2003 | file.write(data, len: sizeof data); |
2004 | file.close(); |
2005 | |
2006 | process.setStandardInputFile(file.fileName()); |
2007 | process.start(command: "testProcessEcho/testProcessEcho" ); |
2008 | |
2009 | QVERIFY(process.waitForFinished()); |
2010 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2011 | QCOMPARE(process.exitCode(), 0); |
2012 | QByteArray all = process.readAll(); |
2013 | QCOMPARE(all.size(), int(sizeof data) - 1); // testProcessEcho drops the ending \0 |
2014 | QVERIFY(all == data); |
2015 | |
2016 | QProcess process2; |
2017 | process2.setStandardInputFile(QProcess::nullDevice()); |
2018 | process2.start(command: "testProcessEcho/testProcessEcho" ); |
2019 | QVERIFY(process2.waitForFinished()); |
2020 | all = process2.readAll(); |
2021 | QCOMPARE(all.size(), 0); |
2022 | } |
2023 | |
2024 | void tst_QProcess::setStandardOutputFile_data() |
2025 | { |
2026 | QTest::addColumn<int>(name: "channelToTest" ); |
2027 | QTest::addColumn<int>(name: "_channelMode" ); |
2028 | QTest::addColumn<bool>(name: "append" ); |
2029 | |
2030 | QTest::newRow(dataTag: "stdout-truncate" ) << int(QProcess::StandardOutput) |
2031 | << int(QProcess::SeparateChannels) |
2032 | << false; |
2033 | QTest::newRow(dataTag: "stdout-append" ) << int(QProcess::StandardOutput) |
2034 | << int(QProcess::SeparateChannels) |
2035 | << true; |
2036 | |
2037 | QTest::newRow(dataTag: "stderr-truncate" ) << int(QProcess::StandardError) |
2038 | << int(QProcess::SeparateChannels) |
2039 | << false; |
2040 | QTest::newRow(dataTag: "stderr-append" ) << int(QProcess::StandardError) |
2041 | << int(QProcess::SeparateChannels) |
2042 | << true; |
2043 | |
2044 | QTest::newRow(dataTag: "merged-truncate" ) << int(QProcess::StandardOutput) |
2045 | << int(QProcess::MergedChannels) |
2046 | << false; |
2047 | QTest::newRow(dataTag: "merged-append" ) << int(QProcess::StandardOutput) |
2048 | << int(QProcess::MergedChannels) |
2049 | << true; |
2050 | } |
2051 | |
2052 | void tst_QProcess::setStandardOutputFile() |
2053 | { |
2054 | static const char data[] = "Original data. " ; |
2055 | static const char testdata[] = "Test data." ; |
2056 | |
2057 | QFETCH(int, channelToTest); |
2058 | QFETCH(int, _channelMode); |
2059 | QFETCH(bool, append); |
2060 | |
2061 | QProcess::ProcessChannelMode channelMode = QProcess::ProcessChannelMode(_channelMode); |
2062 | QIODevice::OpenMode mode = append ? QIODevice::Append : QIODevice::Truncate; |
2063 | |
2064 | // create the destination file with data |
2065 | QFile file(m_temporaryDir.path() + QLatin1String("/data-stdof-" ) + QLatin1String(QTest::currentDataTag())); |
2066 | QVERIFY(file.open(QIODevice::WriteOnly)); |
2067 | file.write(data, len: sizeof data - 1); |
2068 | file.close(); |
2069 | |
2070 | // run the process |
2071 | QProcess process; |
2072 | process.setProcessChannelMode(channelMode); |
2073 | if (channelToTest == QProcess::StandardOutput) |
2074 | process.setStandardOutputFile(fileName: file.fileName(), mode); |
2075 | else |
2076 | process.setStandardErrorFile(fileName: file.fileName(), mode); |
2077 | |
2078 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
2079 | process.write(data: testdata, len: sizeof testdata); |
2080 | QVERIFY(process.waitForFinished()); |
2081 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2082 | QCOMPARE(process.exitCode(), 0); |
2083 | |
2084 | // open the file again and verify the data |
2085 | QVERIFY(file.open(QIODevice::ReadOnly)); |
2086 | QByteArray all = file.readAll(); |
2087 | file.close(); |
2088 | |
2089 | int expectedsize = sizeof testdata - 1; |
2090 | if (mode == QIODevice::Append) { |
2091 | QVERIFY(all.startsWith(data)); |
2092 | expectedsize += sizeof data - 1; |
2093 | } |
2094 | if (channelMode == QProcess::MergedChannels) { |
2095 | expectedsize += sizeof testdata - 1; |
2096 | } else { |
2097 | QVERIFY(all.endsWith(testdata)); |
2098 | } |
2099 | |
2100 | QCOMPARE(all.size(), expectedsize); |
2101 | } |
2102 | |
2103 | void tst_QProcess::setStandardOutputFileNullDevice() |
2104 | { |
2105 | static const char testdata[] = "Test data." ; |
2106 | |
2107 | QProcess process; |
2108 | process.setStandardOutputFile(fileName: QProcess::nullDevice()); |
2109 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
2110 | process.write(data: testdata, len: sizeof testdata); |
2111 | QVERIFY(process.waitForFinished()); |
2112 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2113 | QCOMPARE(process.exitCode(), 0); |
2114 | QCOMPARE(process.bytesAvailable(), Q_INT64_C(0)); |
2115 | |
2116 | QVERIFY(!QFileInfo(QProcess::nullDevice()).isFile()); |
2117 | } |
2118 | |
2119 | void tst_QProcess::setStandardOutputFileAndWaitForBytesWritten() |
2120 | { |
2121 | static const char testdata[] = "Test data." ; |
2122 | |
2123 | QFile file(m_temporaryDir.path() + QLatin1String("/data-stdofawfbw" )); |
2124 | QProcess process; |
2125 | process.setStandardOutputFile(fileName: file.fileName()); |
2126 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
2127 | QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); |
2128 | process.write(data: testdata, len: sizeof testdata); |
2129 | process.waitForBytesWritten(); |
2130 | QVERIFY(process.waitForFinished()); |
2131 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2132 | QCOMPARE(process.exitCode(), 0); |
2133 | |
2134 | // open the file again and verify the data |
2135 | QVERIFY(file.open(QIODevice::ReadOnly)); |
2136 | QByteArray all = file.readAll(); |
2137 | file.close(); |
2138 | |
2139 | QCOMPARE(all, QByteArray::fromRawData(testdata, sizeof testdata - 1)); |
2140 | } |
2141 | |
2142 | void tst_QProcess::setStandardOutputProcess_data() |
2143 | { |
2144 | QTest::addColumn<bool>(name: "merged" ); |
2145 | QTest::addColumn<bool>(name: "waitForBytesWritten" ); |
2146 | QTest::newRow(dataTag: "separate" ) << false << false; |
2147 | QTest::newRow(dataTag: "separate with waitForBytesWritten" ) << false << true; |
2148 | QTest::newRow(dataTag: "merged" ) << true << false; |
2149 | } |
2150 | |
2151 | void tst_QProcess::setStandardOutputProcess() |
2152 | { |
2153 | QProcess source; |
2154 | QProcess sink; |
2155 | |
2156 | QFETCH(bool, merged); |
2157 | QFETCH(bool, waitForBytesWritten); |
2158 | source.setProcessChannelMode(merged ? QProcess::MergedChannels : QProcess::SeparateChannels); |
2159 | source.setStandardOutputProcess(&sink); |
2160 | |
2161 | source.start(command: "testProcessEcho2/testProcessEcho2" ); |
2162 | sink.start(command: "testProcessEcho2/testProcessEcho2" ); |
2163 | |
2164 | QByteArray data("Hello, World" ); |
2165 | source.write(data); |
2166 | if (waitForBytesWritten) |
2167 | source.waitForBytesWritten(); |
2168 | source.closeWriteChannel(); |
2169 | QVERIFY(source.waitForFinished()); |
2170 | QCOMPARE(source.exitStatus(), QProcess::NormalExit); |
2171 | QCOMPARE(source.exitCode(), 0); |
2172 | QVERIFY(sink.waitForFinished()); |
2173 | QCOMPARE(sink.exitStatus(), QProcess::NormalExit); |
2174 | QCOMPARE(sink.exitCode(), 0); |
2175 | QByteArray all = sink.readAll(); |
2176 | |
2177 | if (!merged) |
2178 | QCOMPARE(all, data); |
2179 | else |
2180 | QCOMPARE(all, QByteArray("HHeelllloo,, WWoorrlldd" )); |
2181 | } |
2182 | |
2183 | void tst_QProcess::fileWriterProcess() |
2184 | { |
2185 | const QByteArray line = QByteArrayLiteral(" -- testing testing 1 2 3\n" ); |
2186 | QByteArray stdinStr; |
2187 | stdinStr.reserve(asize: 5000 * (4 + line.size()) + 1); |
2188 | for (int i = 0; i < 5000; ++i) { |
2189 | stdinStr += QByteArray::number(i); |
2190 | stdinStr += line; |
2191 | } |
2192 | |
2193 | QElapsedTimer stopWatch; |
2194 | stopWatch.start(); |
2195 | const QString fileName = m_temporaryDir.path() + QLatin1String("/fileWriterProcess.txt" ); |
2196 | const QString binary = QDir::currentPath() + QLatin1String("/fileWriterProcess/fileWriterProcess" ); |
2197 | |
2198 | do { |
2199 | if (QFile::exists(fileName)) |
2200 | QVERIFY(QFile::remove(fileName)); |
2201 | QProcess process; |
2202 | process.setWorkingDirectory(m_temporaryDir.path()); |
2203 | process.start(command: binary, mode: QIODevice::ReadWrite | QIODevice::Text); |
2204 | process.write(data: stdinStr); |
2205 | process.closeWriteChannel(); |
2206 | while (process.bytesToWrite()) { |
2207 | QVERIFY(stopWatch.elapsed() < 3500); |
2208 | QVERIFY(process.waitForBytesWritten(2000)); |
2209 | } |
2210 | QVERIFY(process.waitForFinished()); |
2211 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2212 | QCOMPARE(process.exitCode(), 0); |
2213 | QCOMPARE(QFile(fileName).size(), qint64(stdinStr.size())); |
2214 | } while (stopWatch.elapsed() < 3000); |
2215 | } |
2216 | |
2217 | void tst_QProcess::detachedProcessParameters_data() |
2218 | { |
2219 | QTest::addColumn<QString>(name: "outChannel" ); |
2220 | QTest::newRow(dataTag: "none" ) << QString(); |
2221 | QTest::newRow(dataTag: "stdout" ) << QString("stdout" ); |
2222 | QTest::newRow(dataTag: "stderr" ) << QString("stderr" ); |
2223 | } |
2224 | |
2225 | void tst_QProcess::detachedProcessParameters() |
2226 | { |
2227 | QFETCH(QString, outChannel); |
2228 | qint64 pid; |
2229 | |
2230 | QFile infoFile(m_temporaryDir.path() + QLatin1String("/detachedinfo.txt" )); |
2231 | if (infoFile.exists()) |
2232 | QVERIFY(infoFile.remove()); |
2233 | QFile channelFile(m_temporaryDir.path() + QLatin1String("detachedinfo2.txt" )); |
2234 | if (channelFile.exists()) |
2235 | QVERIFY(channelFile.remove()); |
2236 | |
2237 | QString workingDir = QDir::currentPath() + "/testDetached" ; |
2238 | |
2239 | QVERIFY(QFile::exists(workingDir)); |
2240 | |
2241 | QVERIFY(qgetenv("tst_QProcess" ).isEmpty()); |
2242 | QByteArray envVarValue("foobarbaz" ); |
2243 | QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); |
2244 | environment.insert(QStringLiteral("tst_QProcess" ), value: QString::fromUtf8(str: envVarValue)); |
2245 | |
2246 | QProcess process; |
2247 | process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached" )); |
2248 | #ifdef Q_OS_WIN |
2249 | int modifierCalls = 0; |
2250 | process.setCreateProcessArgumentsModifier( |
2251 | [&modifierCalls] (QProcess::CreateProcessArguments *) { modifierCalls++; }); |
2252 | #endif |
2253 | QStringList args(infoFile.fileName()); |
2254 | if (!outChannel.isEmpty()) { |
2255 | args << QStringLiteral("--out-channel=" ) + outChannel; |
2256 | if (outChannel == "stdout" ) |
2257 | process.setStandardOutputFile(fileName: channelFile.fileName()); |
2258 | else if (outChannel == "stderr" ) |
2259 | process.setStandardErrorFile(fileName: channelFile.fileName()); |
2260 | } |
2261 | process.setArguments(args); |
2262 | process.setWorkingDirectory(workingDir); |
2263 | process.setProcessEnvironment(environment); |
2264 | QVERIFY(process.startDetached(&pid)); |
2265 | |
2266 | QFileInfo fi(infoFile); |
2267 | fi.setCaching(false); |
2268 | //The guard counter ensures the test does not hang if the sub process fails. |
2269 | //Instead, the test will fail when trying to open & verify the sub process output file. |
2270 | for (int guard = 0; guard < 100 && fi.size() == 0; guard++) { |
2271 | QTest::qSleep(ms: 100); |
2272 | } |
2273 | |
2274 | QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text)); |
2275 | QString actualWorkingDir = QString::fromUtf8(str: infoFile.readLine()).trimmed(); |
2276 | QByteArray processIdString = infoFile.readLine().trimmed(); |
2277 | QByteArray actualEnvVarValue = infoFile.readLine().trimmed(); |
2278 | QByteArray infoFileContent; |
2279 | if (!outChannel.isEmpty()) { |
2280 | infoFile.seek(offset: 0); |
2281 | infoFileContent = infoFile.readAll(); |
2282 | } |
2283 | infoFile.close(); |
2284 | infoFile.remove(); |
2285 | |
2286 | if (!outChannel.isEmpty()) { |
2287 | QVERIFY(channelFile.open(QIODevice::ReadOnly | QIODevice::Text)); |
2288 | QByteArray channelContent = channelFile.readAll(); |
2289 | channelFile.close(); |
2290 | channelFile.remove(); |
2291 | QCOMPARE(channelContent, infoFileContent); |
2292 | } |
2293 | |
2294 | bool ok = false; |
2295 | qint64 actualPid = processIdString.toLongLong(ok: &ok); |
2296 | QVERIFY(ok); |
2297 | |
2298 | QCOMPARE(actualWorkingDir, workingDir); |
2299 | QCOMPARE(actualPid, pid); |
2300 | QCOMPARE(actualEnvVarValue, envVarValue); |
2301 | #ifdef Q_OS_WIN |
2302 | QCOMPARE(modifierCalls, 1); |
2303 | #endif |
2304 | } |
2305 | |
2306 | void tst_QProcess::switchReadChannels() |
2307 | { |
2308 | const char data[] = "ABCD" ; |
2309 | |
2310 | QProcess process; |
2311 | |
2312 | process.start(command: "testProcessEcho2/testProcessEcho2" ); |
2313 | process.write(data); |
2314 | process.closeWriteChannel(); |
2315 | QVERIFY(process.waitForFinished(5000)); |
2316 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2317 | QCOMPARE(process.exitCode(), 0); |
2318 | |
2319 | for (int i = 0; i < 4; ++i) { |
2320 | process.setReadChannel(QProcess::StandardOutput); |
2321 | QCOMPARE(process.read(1), QByteArray(&data[i], 1)); |
2322 | process.setReadChannel(QProcess::StandardError); |
2323 | QCOMPARE(process.read(1), QByteArray(&data[i], 1)); |
2324 | } |
2325 | |
2326 | process.ungetChar(c: 'D'); |
2327 | process.setReadChannel(QProcess::StandardOutput); |
2328 | process.ungetChar(c: 'D'); |
2329 | process.setReadChannel(QProcess::StandardError); |
2330 | QCOMPARE(process.read(1), QByteArray("D" )); |
2331 | process.setReadChannel(QProcess::StandardOutput); |
2332 | QCOMPARE(process.read(1), QByteArray("D" )); |
2333 | } |
2334 | |
2335 | void tst_QProcess::discardUnwantedOutput() |
2336 | { |
2337 | QProcess process; |
2338 | |
2339 | process.setProgram("testProcessEcho2/testProcessEcho2" ); |
2340 | process.start(mode: QIODevice::WriteOnly); |
2341 | process.write(data: "Hello, World" ); |
2342 | process.closeWriteChannel(); |
2343 | QVERIFY(process.waitForFinished(5000)); |
2344 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2345 | QCOMPARE(process.exitCode(), 0); |
2346 | |
2347 | process.setReadChannel(QProcess::StandardOutput); |
2348 | QCOMPARE(process.bytesAvailable(), Q_INT64_C(0)); |
2349 | process.setReadChannel(QProcess::StandardError); |
2350 | QCOMPARE(process.bytesAvailable(), Q_INT64_C(0)); |
2351 | } |
2352 | |
2353 | // Q_OS_WIN - setWorkingDirectory will chdir before starting the process on unices |
2354 | void tst_QProcess::setWorkingDirectory() |
2355 | { |
2356 | QProcess process; |
2357 | process.setWorkingDirectory("test" ); |
2358 | |
2359 | // use absolute path because on Windows, the executable is relative to the parent's CWD |
2360 | // while on Unix with fork it's relative to the child's (with posix_spawn, it could be either). |
2361 | process.start(command: QFileInfo("testSetWorkingDirectory/testSetWorkingDirectory" ).absoluteFilePath()); |
2362 | |
2363 | QVERIFY2(process.waitForFinished(), process.errorString().toLocal8Bit()); |
2364 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2365 | QCOMPARE(process.exitCode(), 0); |
2366 | |
2367 | QByteArray workingDir = process.readAllStandardOutput(); |
2368 | QCOMPARE(QDir("test" ).canonicalPath(), QDir(workingDir.constData()).canonicalPath()); |
2369 | } |
2370 | |
2371 | void tst_QProcess::setNonExistentWorkingDirectory() |
2372 | { |
2373 | QProcess process; |
2374 | process.setWorkingDirectory("this/directory/should/not/exist/for/sure" ); |
2375 | |
2376 | // use absolute path because on Windows, the executable is relative to the parent's CWD |
2377 | // while on Unix with fork it's relative to the child's (with posix_spawn, it could be either). |
2378 | process.start(command: QFileInfo("testSetWorkingDirectory/testSetWorkingDirectory" ).absoluteFilePath()); |
2379 | QVERIFY(!process.waitForFinished()); |
2380 | QCOMPARE(int(process.error()), int(QProcess::FailedToStart)); |
2381 | |
2382 | #ifdef Q_OS_UNIX |
2383 | # ifdef QPROCESS_USE_SPAWN |
2384 | QEXPECT_FAIL("" , "QProcess cannot detect failure to start when using posix_spawn()" , Continue); |
2385 | # endif |
2386 | QVERIFY2(process.errorString().startsWith("chdir:" ), process.errorString().toLocal8Bit()); |
2387 | #endif |
2388 | } |
2389 | |
2390 | void tst_QProcess::startFinishStartFinish() |
2391 | { |
2392 | QProcess process; |
2393 | |
2394 | for (int i = 0; i < 3; ++i) { |
2395 | QCOMPARE(process.state(), QProcess::NotRunning); |
2396 | |
2397 | process.start(command: "testProcessOutput/testProcessOutput" ); |
2398 | QVERIFY(process.waitForReadyRead(10000)); |
2399 | QCOMPARE(QString::fromLatin1(process.readLine().trimmed()), |
2400 | QString("0 -this is a number" )); |
2401 | if (process.state() != QProcess::NotRunning) { |
2402 | QVERIFY(process.waitForFinished(10000)); |
2403 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2404 | QCOMPARE(process.exitCode(), 0); |
2405 | } |
2406 | } |
2407 | } |
2408 | |
2409 | void tst_QProcess::invalidProgramString_data() |
2410 | { |
2411 | QTest::addColumn<QString>(name: "programString" ); |
2412 | QTest::newRow(dataTag: "null string" ) << QString(); |
2413 | QTest::newRow(dataTag: "empty string" ) << QString("" ); |
2414 | QTest::newRow(dataTag: "only blank string" ) << QString(" " ); |
2415 | } |
2416 | |
2417 | void tst_QProcess::invalidProgramString() |
2418 | { |
2419 | QFETCH(QString, programString); |
2420 | QProcess process; |
2421 | |
2422 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
2423 | QSignalSpy spy(&process, &QProcess::errorOccurred); |
2424 | QVERIFY(spy.isValid()); |
2425 | #if QT_DEPRECATED_SINCE(5, 6) |
2426 | QSignalSpy spy2(&process, static_cast<QProcessErrorSignal>(&QProcess::error)); |
2427 | QVERIFY(spy2.isValid()); |
2428 | #endif |
2429 | |
2430 | process.start(command: programString); |
2431 | QCOMPARE(process.error(), QProcess::FailedToStart); |
2432 | QCOMPARE(spy.count(), 1); |
2433 | #if QT_DEPRECATED_SINCE(5, 6) |
2434 | QCOMPARE(spy2.count(), 1); |
2435 | #endif |
2436 | |
2437 | QVERIFY(!QProcess::startDetached(programString)); |
2438 | } |
2439 | |
2440 | void tst_QProcess::onlyOneStartedSignal() |
2441 | { |
2442 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
2443 | QProcess process; |
2444 | |
2445 | QSignalSpy spyStarted(&process, &QProcess::started); |
2446 | QSignalSpy spyFinished(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
2447 | |
2448 | QVERIFY(spyStarted.isValid()); |
2449 | QVERIFY(spyFinished.isValid()); |
2450 | |
2451 | process.start(command: "testProcessNormal/testProcessNormal" ); |
2452 | QVERIFY(process.waitForStarted(5000)); |
2453 | QVERIFY(process.waitForFinished(5000)); |
2454 | QCOMPARE(spyStarted.count(), 1); |
2455 | QCOMPARE(spyFinished.count(), 1); |
2456 | |
2457 | spyStarted.clear(); |
2458 | spyFinished.clear(); |
2459 | |
2460 | process.start(command: "testProcessNormal/testProcessNormal" ); |
2461 | QVERIFY(process.waitForFinished(5000)); |
2462 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2463 | QCOMPARE(process.exitCode(), 0); |
2464 | QCOMPARE(spyStarted.count(), 1); |
2465 | QCOMPARE(spyFinished.count(), 1); |
2466 | } |
2467 | |
2468 | class BlockOnReadStdOut : public QObject |
2469 | { |
2470 | Q_OBJECT |
2471 | public: |
2472 | BlockOnReadStdOut(QProcess *process) |
2473 | { |
2474 | connect(sender: process, signal: &QProcess::readyReadStandardOutput, receiver: this, slot: &BlockOnReadStdOut::block); |
2475 | } |
2476 | |
2477 | public slots: |
2478 | void block() |
2479 | { |
2480 | QThread::sleep(1); |
2481 | } |
2482 | }; |
2483 | |
2484 | void tst_QProcess::finishProcessBeforeReadingDone() |
2485 | { |
2486 | QProcess process; |
2487 | BlockOnReadStdOut blocker(&process); |
2488 | QEventLoop loop; |
2489 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal2>(&QProcess::finished), |
2490 | receiver: &loop, slot: &QEventLoop::quit); |
2491 | process.start(command: "testProcessOutput/testProcessOutput" ); |
2492 | QVERIFY(process.waitForStarted()); |
2493 | loop.exec(); |
2494 | QStringList lines = QString::fromLocal8Bit(str: process.readAllStandardOutput()).split( |
2495 | sep: QRegExp(QStringLiteral("[\r\n]" )), behavior: Qt::SkipEmptyParts); |
2496 | QVERIFY(!lines.isEmpty()); |
2497 | QCOMPARE(lines.last(), QStringLiteral("10239 -this is a number" )); |
2498 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2499 | QCOMPARE(process.exitCode(), 0); |
2500 | } |
2501 | |
2502 | //----------------------------------------------------------------------------- |
2503 | void tst_QProcess::waitForStartedWithoutStart() |
2504 | { |
2505 | QProcess process; |
2506 | QVERIFY(!process.waitForStarted(5000)); |
2507 | } |
2508 | |
2509 | //----------------------------------------------------------------------------- |
2510 | void tst_QProcess::startStopStartStop() |
2511 | { |
2512 | // we actually do start-stop x 3 :-) |
2513 | QProcess process; |
2514 | process.start(command: "testProcessNormal/testProcessNormal" ); |
2515 | QVERIFY(process.waitForFinished()); |
2516 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2517 | QCOMPARE(process.exitCode(), 0); |
2518 | |
2519 | process.start(program: "testExitCodes/testExitCodes" , arguments: QStringList() << "1" ); |
2520 | QVERIFY(process.waitForFinished()); |
2521 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2522 | QCOMPARE(process.exitCode(), 1); |
2523 | |
2524 | process.start(command: "testProcessNormal/testProcessNormal" ); |
2525 | QVERIFY(process.waitForFinished()); |
2526 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2527 | QCOMPARE(process.exitCode(), 0); |
2528 | } |
2529 | |
2530 | //----------------------------------------------------------------------------- |
2531 | void tst_QProcess::startStopStartStopBuffers_data() |
2532 | { |
2533 | QTest::addColumn<int>(name: "channelMode1" ); |
2534 | QTest::addColumn<int>(name: "channelMode2" ); |
2535 | |
2536 | QTest::newRow(dataTag: "separate-separate" ) << int(QProcess::SeparateChannels) << int(QProcess::SeparateChannels); |
2537 | QTest::newRow(dataTag: "separate-merged" ) << int(QProcess::SeparateChannels) << int(QProcess::MergedChannels); |
2538 | QTest::newRow(dataTag: "merged-separate" ) << int(QProcess::MergedChannels) << int(QProcess::SeparateChannels); |
2539 | QTest::newRow(dataTag: "merged-merged" ) << int(QProcess::MergedChannels) << int(QProcess::MergedChannels); |
2540 | QTest::newRow(dataTag: "merged-forwarded" ) << int(QProcess::MergedChannels) << int(QProcess::ForwardedChannels); |
2541 | } |
2542 | |
2543 | void tst_QProcess::startStopStartStopBuffers() |
2544 | { |
2545 | QFETCH(int, channelMode1); |
2546 | QFETCH(int, channelMode2); |
2547 | |
2548 | QProcess process; |
2549 | process.setProcessChannelMode(QProcess::ProcessChannelMode(channelMode1)); |
2550 | process.start(command: "testProcessHang/testProcessHang" ); |
2551 | QVERIFY2(process.waitForReadyRead(), process.errorString().toLocal8Bit()); |
2552 | if (channelMode1 == QProcess::SeparateChannels || channelMode1 == QProcess::ForwardedOutputChannel) { |
2553 | process.setReadChannel(QProcess::StandardError); |
2554 | if (process.bytesAvailable() == 0) |
2555 | QVERIFY(process.waitForReadyRead()); |
2556 | process.setReadChannel(QProcess::StandardOutput); |
2557 | } |
2558 | |
2559 | // We want to test that the write buffer still has bytes after the child |
2560 | // exiting. We do that by writing to a child process that never reads. We |
2561 | // just have to write more data than a pipe can hold, so that even if |
2562 | // QProcess finds the pipe writable (during waitForFinished() or in the |
2563 | // QWindowsPipeWriter thread), some data will remain. The worst case I know |
2564 | // of is Linux, which defaults to 64 kB of buffer. |
2565 | |
2566 | process.write(data: QByteArray(128 * 1024, 'a')); |
2567 | QVERIFY(process.bytesToWrite() > 0); |
2568 | process.kill(); |
2569 | |
2570 | QVERIFY(process.waitForFinished()); |
2571 | |
2572 | #ifndef Q_OS_WIN |
2573 | // confirm that our buffers are still full |
2574 | // Note: this doesn't work on Windows because our buffers are drained into |
2575 | // QWindowsPipeWriter before being sent to the child process. |
2576 | QVERIFY(process.bytesToWrite() > 0); |
2577 | QVERIFY(process.bytesAvailable() > 0); // channelMode1 is not ForwardedChannels |
2578 | if (channelMode1 == QProcess::SeparateChannels || channelMode1 == QProcess::ForwardedOutputChannel) { |
2579 | process.setReadChannel(QProcess::StandardError); |
2580 | QVERIFY(process.bytesAvailable() > 0); |
2581 | process.setReadChannel(QProcess::StandardOutput); |
2582 | } |
2583 | #endif |
2584 | |
2585 | process.setProcessChannelMode(QProcess::ProcessChannelMode(channelMode2)); |
2586 | process.start(command: "testProcessEcho2/testProcessEcho2" , mode: QIODevice::ReadWrite | QIODevice::Text); |
2587 | |
2588 | // the buffers should now be empty |
2589 | QCOMPARE(process.bytesToWrite(), qint64(0)); |
2590 | QCOMPARE(process.bytesAvailable(), qint64(0)); |
2591 | process.setReadChannel(QProcess::StandardError); |
2592 | QCOMPARE(process.bytesAvailable(), qint64(0)); |
2593 | process.setReadChannel(QProcess::StandardOutput); |
2594 | |
2595 | process.write(data: "line3\n" ); |
2596 | process.closeWriteChannel(); |
2597 | QVERIFY(process.waitForFinished()); |
2598 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2599 | QCOMPARE(process.exitCode(), 0); |
2600 | |
2601 | if (channelMode2 == QProcess::MergedChannels) { |
2602 | QCOMPARE(process.readAll(), QByteArray("lliinnee33\n\n" )); |
2603 | } else if (channelMode2 != QProcess::ForwardedChannels) { |
2604 | QCOMPARE(process.readAllStandardOutput(), QByteArray("line3\n" )); |
2605 | if (channelMode2 == QProcess::SeparateChannels) |
2606 | QCOMPARE(process.readAllStandardError(), QByteArray("line3\n" )); |
2607 | } |
2608 | } |
2609 | |
2610 | void tst_QProcess::processEventsInAReadyReadSlot_data() |
2611 | { |
2612 | QTest::addColumn<bool>(name: "callWaitForReadyRead" ); |
2613 | |
2614 | QTest::newRow(dataTag: "no waitForReadyRead" ) << false; |
2615 | QTest::newRow(dataTag: "waitForReadyRead" ) << true; |
2616 | } |
2617 | |
2618 | void tst_QProcess::processEventsInAReadyReadSlot() |
2619 | { |
2620 | // Test whether processing events in a readyReadXXX slot crashes. (QTBUG-48697) |
2621 | QFETCH(bool, callWaitForReadyRead); |
2622 | QProcess process; |
2623 | QObject::connect(sender: &process, signal: &QProcess::readyReadStandardOutput, |
2624 | receiver: this, slot: &tst_QProcess::processApplicationEvents); |
2625 | process.start(command: "testProcessEcho/testProcessEcho" ); |
2626 | QVERIFY(process.waitForStarted()); |
2627 | const QByteArray data(156, 'x'); |
2628 | process.write(data: data.constData(), len: data.size() + 1); |
2629 | if (callWaitForReadyRead) |
2630 | QVERIFY(process.waitForReadyRead()); |
2631 | if (process.state() == QProcess::Running) |
2632 | QVERIFY(process.waitForFinished()); |
2633 | } |
2634 | |
2635 | #if QT_DEPRECATED_SINCE(5, 13) |
2636 | |
2637 | void tst_QProcess::crashTest2_deprecated() |
2638 | { |
2639 | QProcess process; |
2640 | process.start(command: "testProcessCrash/testProcessCrash" ); |
2641 | QVERIFY(process.waitForStarted(5000)); |
2642 | |
2643 | qRegisterMetaType<QProcess::ProcessError>(typeName: "QProcess::ProcessError" ); |
2644 | qRegisterMetaType<QProcess::ExitStatus>(typeName: "QProcess::ExitStatus" ); |
2645 | |
2646 | QSignalSpy spy(&process, static_cast<QProcessErrorSignal>(&QProcess::errorOccurred)); |
2647 | QSignalSpy spy2(&process, static_cast<QProcessFinishedSignal2>(&QProcess::finished)); |
2648 | |
2649 | QVERIFY(spy.isValid()); |
2650 | QVERIFY(spy2.isValid()); |
2651 | |
2652 | QObject::connect(sender: &process, signal: static_cast<QProcessFinishedSignal1>(&QProcess::finished), |
2653 | receiver: this, slot: &tst_QProcess::exitLoopSlot); |
2654 | |
2655 | QTestEventLoop::instance().enterLoop(secs: 30); |
2656 | if (QTestEventLoop::instance().timeout()) |
2657 | QFAIL("Failed to detect crash : operation timed out" ); |
2658 | |
2659 | QCOMPARE(spy.count(), 1); |
2660 | QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed); |
2661 | |
2662 | QCOMPARE(spy2.count(), 1); |
2663 | QCOMPARE(*static_cast<const QProcess::ExitStatus *>(spy2.at(0).at(1).constData()), QProcess::CrashExit); |
2664 | |
2665 | QCOMPARE(process.exitStatus(), QProcess::CrashExit); |
2666 | } |
2667 | |
2668 | void tst_QProcess::restartProcessDeadlock_deprecated() |
2669 | { |
2670 | // The purpose of this test is to detect whether restarting a |
2671 | // process in the finished() connected slot causes a deadlock |
2672 | // because of the way QProcessManager uses its locks. |
2673 | QProcess process; |
2674 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal1>(&QProcess::finished), |
2675 | receiver: this, slot: &tst_QProcess::restartProcess); |
2676 | |
2677 | process.start(command: "testProcessEcho/testProcessEcho" ); |
2678 | |
2679 | QCOMPARE(process.write("" , 1), qlonglong(1)); |
2680 | QVERIFY(process.waitForFinished(5000)); |
2681 | |
2682 | QObject::disconnect(sender: &process, signal: static_cast<QProcessFinishedSignal1>(&QProcess::finished), receiver: nullptr, zero: nullptr); |
2683 | |
2684 | QCOMPARE(process.write("" , 1), qlonglong(1)); |
2685 | QVERIFY(process.waitForFinished(5000)); |
2686 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2687 | QCOMPARE(process.exitCode(), 0); |
2688 | } |
2689 | |
2690 | void tst_QProcess::waitForReadyReadInAReadyReadSlot_deprecated() |
2691 | { |
2692 | QProcess process; |
2693 | connect(sender: &process, signal: &QIODevice::readyRead, receiver: this, slot: &tst_QProcess::waitForReadyReadInAReadyReadSlotSlot); |
2694 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal1>(&QProcess::finished), |
2695 | receiver: this, slot: &tst_QProcess::exitLoopSlot); |
2696 | bytesAvailable = 0; |
2697 | |
2698 | process.start(command: "testProcessEcho/testProcessEcho" ); |
2699 | QVERIFY(process.waitForStarted(5000)); |
2700 | |
2701 | QSignalSpy spy(&process, &QProcess::readyRead); |
2702 | QVERIFY(spy.isValid()); |
2703 | process.write(data: "foo" ); |
2704 | QTestEventLoop::instance().enterLoop(secs: 30); |
2705 | QVERIFY(!QTestEventLoop::instance().timeout()); |
2706 | |
2707 | QCOMPARE(spy.count(), 1); |
2708 | |
2709 | process.disconnect(); |
2710 | QVERIFY(process.waitForFinished(5000)); |
2711 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2712 | QCOMPARE(process.exitCode(), 0); |
2713 | QVERIFY(process.bytesAvailable() > bytesAvailable); |
2714 | } |
2715 | |
2716 | void tst_QProcess::finishProcessBeforeReadingDone_deprecated() |
2717 | { |
2718 | QProcess process; |
2719 | BlockOnReadStdOut blocker(&process); |
2720 | QEventLoop loop; |
2721 | connect(sender: &process, signal: static_cast<QProcessFinishedSignal1>(&QProcess::finished), |
2722 | receiver: &loop, slot: &QEventLoop::quit); |
2723 | process.start(command: "testProcessOutput/testProcessOutput" ); |
2724 | QVERIFY(process.waitForStarted()); |
2725 | loop.exec(); |
2726 | QStringList lines = QString::fromLocal8Bit(str: process.readAllStandardOutput()).split( |
2727 | sep: QRegExp(QStringLiteral("[\r\n]" )), behavior: Qt::SkipEmptyParts); |
2728 | QVERIFY(!lines.isEmpty()); |
2729 | QCOMPARE(lines.last(), QStringLiteral("10239 -this is a number" )); |
2730 | QCOMPARE(process.exitStatus(), QProcess::NormalExit); |
2731 | QCOMPARE(process.exitCode(), 0); |
2732 | } |
2733 | |
2734 | #endif |
2735 | |
2736 | enum class ChdirMode { |
2737 | None = 0, |
2738 | InParent, |
2739 | InChild |
2740 | }; |
2741 | Q_DECLARE_METATYPE(ChdirMode) |
2742 | |
2743 | void tst_QProcess::startFromCurrentWorkingDir_data() |
2744 | { |
2745 | qRegisterMetaType<ChdirMode>(); |
2746 | QTest::addColumn<QString>(name: "programPrefix" ); |
2747 | QTest::addColumn<ChdirMode>(name: "chdirMode" ); |
2748 | QTest::addColumn<bool>(name: "success" ); |
2749 | |
2750 | constexpr bool IsWindows = true |
2751 | #ifdef Q_OS_UNIX |
2752 | && false |
2753 | #endif |
2754 | ; |
2755 | |
2756 | // baseline: trying to execute the directory, this can't possibly succeed! |
2757 | QTest::newRow(dataTag: "plain-same-cwd" ) << QString() << ChdirMode::None << false; |
2758 | |
2759 | // cross-platform behavior: neither OS searches the setWorkingDirectory() |
2760 | // dir without "./" |
2761 | QTest::newRow(dataTag: "plain-child-chdir" ) << QString() << ChdirMode::InChild << false; |
2762 | |
2763 | // cross-platform behavior: both OSes search the parent's CWD with "./" |
2764 | QTest::newRow(dataTag: "prefixed-parent-chdir" ) << "./" << ChdirMode::InParent << true; |
2765 | |
2766 | // opposite behaviors: Windows searches the parent's CWD and Unix searches |
2767 | // the child's with "./" |
2768 | QTest::newRow(dataTag: "prefixed-child-chdir" ) << "./" << ChdirMode::InChild << !IsWindows; |
2769 | |
2770 | // Windows searches the parent's CWD without "./" |
2771 | QTest::newRow(dataTag: "plain-parent-chdir" ) << QString() << ChdirMode::InParent << IsWindows; |
2772 | } |
2773 | |
2774 | void tst_QProcess::startFromCurrentWorkingDir() |
2775 | { |
2776 | QFETCH(QString, programPrefix); |
2777 | QFETCH(ChdirMode, chdirMode); |
2778 | QFETCH(bool, success); |
2779 | |
2780 | QProcess process; |
2781 | qRegisterMetaType<QProcess::ProcessError>(); |
2782 | QSignalSpy errorSpy(&process, &QProcess::errorOccurred); |
2783 | QVERIFY(errorSpy.isValid()); |
2784 | |
2785 | // both the dir name and the executable name |
2786 | const QString target = QStringLiteral("testProcessNormal" ); |
2787 | process.setProgram(programPrefix + target); |
2788 | |
2789 | #ifdef Q_OS_UNIX |
2790 | // Reset PATH, to be sure it doesn't contain . or the empty path. |
2791 | // We can't do this on Windows because DLLs are searched in PATH |
2792 | // and Windows always searches "." anyway. |
2793 | auto restoreEnv = qScopeGuard(f: [old = qgetenv(varName: "PATH" )] { |
2794 | qputenv(varName: "PATH" , value: old); |
2795 | }); |
2796 | qputenv(varName: "PATH" , value: "/" ); |
2797 | #endif |
2798 | |
2799 | switch (chdirMode) { |
2800 | case ChdirMode::InParent: { |
2801 | auto restoreCwd = qScopeGuard(f: [old = QDir::currentPath()] { |
2802 | QDir::setCurrent(old); |
2803 | }); |
2804 | QVERIFY(QDir::setCurrent(target)); |
2805 | process.start(); |
2806 | break; |
2807 | } |
2808 | case ChdirMode::InChild: |
2809 | process.setWorkingDirectory(target); |
2810 | Q_FALLTHROUGH(); |
2811 | case ChdirMode::None: |
2812 | process.start(); |
2813 | break; |
2814 | } |
2815 | |
2816 | QCOMPARE(process.waitForStarted(), success); |
2817 | QCOMPARE(errorSpy.count(), int(!success)); |
2818 | if (success) { |
2819 | QVERIFY(process.waitForFinished()); |
2820 | } else { |
2821 | QCOMPARE(process.error(), QProcess::FailedToStart); |
2822 | } |
2823 | } |
2824 | |
2825 | QTEST_MAIN(tst_QProcess) |
2826 | #include "tst_qprocess.moc" |
2827 | |