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
52typedef void (QProcess::*QProcessFinishedSignal1)(int);
53typedef void (QProcess::*QProcessFinishedSignal2)(int, QProcess::ExitStatus);
54typedef void (QProcess::*QProcessErrorSignal)(QProcess::ProcessError);
55
56class tst_QProcess : public QObject
57{
58 Q_OBJECT
59
60public slots:
61 void initTestCase();
62 void cleanupTestCase();
63 void init();
64
65private 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
171protected slots:
172 void readFromProcess();
173 void exitLoopSlot();
174 void processApplicationEvents();
175 void restartProcess();
176 void waitForReadyReadInAReadyReadSlotSlot();
177 void waitForBytesWrittenInABytesWrittenSlotSlot();
178
179private:
180 qint64 bytesAvailable;
181 QTemporaryDir m_temporaryDir;
182};
183
184void 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
192void tst_QProcess::cleanupTestCase()
193{
194}
195
196void tst_QProcess::init()
197{
198 bytesAvailable = 0;
199}
200
201// Testing get/set functions
202void 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
230void 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
263void 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
290void 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
335void 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
352void 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
370void tst_QProcess::execute()
371{
372 QCOMPARE(QProcess::execute("testProcessNormal/testProcessNormal",
373 QStringList() << "arg1" << "arg2"), 0);
374 QCOMPARE(QProcess::execute("nonexistingexe"), -2);
375}
376
377void tst_QProcess::startDetached()
378{
379 QVERIFY(QProcess::startDetached("testProcessNormal/testProcessNormal",
380 QStringList() << "arg1" << "arg2"));
381 QCOMPARE(QProcess::startDetached("nonexistingexe"), false);
382}
383
384void 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
395void 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
441void 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
472void 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
486void 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
527void tst_QProcess::exitLoopSlot()
528{
529 QTestEventLoop::instance().exitLoop();
530}
531
532void tst_QProcess::processApplicationEvents()
533{
534 QCoreApplication::processEvents();
535}
536
537void 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)
586void 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
604void 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)
617void 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
626void 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
645void 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
669void 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
685void 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
706void 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
741void 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
758void 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
776void 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
798void tst_QProcess::restartProcess()
799{
800 QProcess *process = qobject_cast<QProcess *>(object: sender());
801 QVERIFY(process);
802 process->start(command: "testProcessEcho/testProcessEcho");
803}
804
805void 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
834void 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
864void 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
905void 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
939void 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
963void 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
981class SoftExitProcess : public QProcess
982{
983 Q_OBJECT
984public:
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
1031public 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
1056private:
1057 void writePendingData()
1058 {
1059 if (!dataToWrite.isEmpty()) {
1060 write(data: dataToWrite);
1061 dataToWrite.clear();
1062 }
1063 }
1064
1065private:
1066 int n;
1067 bool killing;
1068 QByteArray dataToWrite;
1069};
1070
1071void 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
1091void 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
1103void 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
1126void 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
1160void 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
1198void 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
1220class TestThread : public QThread
1221{
1222 Q_OBJECT
1223public:
1224 inline int code()
1225 {
1226 return exitCode;
1227 }
1228
1229protected:
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
1244protected slots:
1245 inline void catchExitCode(int exitCode)
1246 {
1247 this->exitCode = exitCode;
1248 exit(retcode: exitCode);
1249 }
1250
1251private:
1252 int exitCode;
1253};
1254
1255void 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
1265void 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
1289void 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
1303void 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
1329void 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
1339void 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
1362void 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
1371void 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
1414static 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
1423void 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
1481void 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
1505void 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
1526void 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
1537void 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
1617void 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
1655void 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
1697void 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
1705void 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
1740void 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
1758void 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
1775void 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
1833void tst_QProcess::setProcessEnvironment_data()
1834{
1835 setEnvironment_data();
1836}
1837
1838void 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
1871void 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
1912void 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
1921void 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
1932void 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
1945void 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
1960void 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
1996void 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
2024void 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
2052void 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
2103void 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
2119void 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
2142void 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
2151void 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
2183void 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
2217void 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
2225void 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
2306void 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
2335void 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
2354void 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
2371void 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
2390void 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
2409void 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
2417void 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
2440void 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
2468class BlockOnReadStdOut : public QObject
2469{
2470 Q_OBJECT
2471public:
2472 BlockOnReadStdOut(QProcess *process)
2473 {
2474 connect(sender: process, signal: &QProcess::readyReadStandardOutput, receiver: this, slot: &BlockOnReadStdOut::block);
2475 }
2476
2477public slots:
2478 void block()
2479 {
2480 QThread::sleep(1);
2481 }
2482};
2483
2484void 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//-----------------------------------------------------------------------------
2503void tst_QProcess::waitForStartedWithoutStart()
2504{
2505 QProcess process;
2506 QVERIFY(!process.waitForStarted(5000));
2507}
2508
2509//-----------------------------------------------------------------------------
2510void 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//-----------------------------------------------------------------------------
2531void 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
2543void 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
2610void 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
2618void 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
2637void 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
2668void 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
2690void 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
2716void 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
2736enum class ChdirMode {
2737 None = 0,
2738 InParent,
2739 InChild
2740};
2741Q_DECLARE_METATYPE(ChdirMode)
2742
2743void 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
2774void 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
2825QTEST_MAIN(tst_QProcess)
2826#include "tst_qprocess.moc"
2827

source code of qtbase/tests/auto/corelib/io/qprocess/tst_qprocess.cpp