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