summaryrefslogtreecommitdiffstats
path: root/tests/auto/qprocess/tst_qprocess.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /tests/auto/qprocess/tst_qprocess.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'tests/auto/qprocess/tst_qprocess.cpp')
-rw-r--r--tests/auto/qprocess/tst_qprocess.cpp2448
1 files changed, 2448 insertions, 0 deletions
diff --git a/tests/auto/qprocess/tst_qprocess.cpp b/tests/auto/qprocess/tst_qprocess.cpp
new file mode 100644
index 0000000000..2a8874eab3
--- /dev/null
+++ b/tests/auto/qprocess/tst_qprocess.cpp
@@ -0,0 +1,2448 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include <QtCore/QProcess>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QThread>
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+#include <QtCore/QMetaType>
+#if !defined(Q_OS_SYMBIAN)
+// Network test unnecessary?
+#include <QtNetwork/QHostInfo>
+#endif
+#include <stdlib.h>
+
+#ifdef QT_NO_PROCESS
+QTEST_NOOP_MAIN
+#else
+
+#if defined(Q_OS_WIN)
+#include <windows.h>
+#endif
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+Q_DECLARE_METATYPE(QList<QProcess::ExitStatus>);
+Q_DECLARE_METATYPE(QProcess::ExitStatus);
+Q_DECLARE_METATYPE(QProcess::ProcessState);
+
+#define QPROCESS_VERIFY(Process, Fn) \
+{ \
+const bool ret = Process.Fn; \
+if (ret == false) \
+ qWarning("QProcess error: %d: %s", Process.error(), qPrintable(Process.errorString())); \
+QVERIFY(ret); \
+}
+
+class tst_QProcess : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QProcess();
+ virtual ~tst_QProcess();
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void getSetCheck();
+ void constructing();
+ void simpleStart();
+ void execute();
+ void startDetached();
+ void crashTest();
+ void crashTest2();
+ void echoTest_data();
+ void echoTest();
+ void echoTest2();
+ void echoTest_performance();
+#if defined Q_OS_WIN
+ void echoTestGui();
+ void batFiles_data();
+ void batFiles();
+#endif
+ void exitStatus_data();
+ void exitStatus();
+ void loopBackTest();
+ void readTimeoutAndThenCrash();
+ void waitForFinished();
+ void deadWhileReading();
+ void restartProcessDeadlock();
+ void closeWriteChannel();
+ void closeReadChannel();
+ void openModes();
+ void emitReadyReadOnlyWhenNewDataArrives();
+ void hardExit();
+ void softExit();
+ void softExitInSlots_data();
+ void softExitInSlots();
+ void mergedChannels();
+ void forwardedChannels();
+ void atEnd();
+ void atEnd2();
+ void processInAThread();
+ void processesInMultipleThreads();
+ void waitForFinishedWithTimeout();
+ void waitForReadyReadInAReadyReadSlot();
+ void waitForBytesWrittenInABytesWrittenSlot();
+ void spaceArgsTest_data();
+ void spaceArgsTest();
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+ void nativeArguments();
+#endif
+ void exitCodeTest();
+ void setEnvironment_data();
+ void setEnvironment();
+ void setProcessEnvironment_data();
+ void setProcessEnvironment();
+ void systemEnvironment();
+ void spaceInName();
+ void lockupsInStartDetached();
+ void waitForReadyReadForNonexistantProcess();
+ void setStandardInputFile();
+ void setStandardOutputFile_data();
+ void setStandardOutputFile();
+ void setStandardOutputProcess_data();
+ void setStandardOutputProcess();
+ void removeFileWhileProcessIsRunning();
+ void fileWriterProcess();
+ void detachedWorkingDirectoryAndPid();
+ void switchReadChannels();
+ void setWorkingDirectory();
+ void startFinishStartFinish();
+ void invalidProgramString_data();
+ void invalidProgramString();
+
+ // keep these at the end, since they use lots of processes and sometimes
+ // caused obscure failures to occur in tests that followed them (esp. on the Mac)
+ void failToStart();
+ void failToStartWithWait();
+ void failToStartWithEventLoop();
+
+protected slots:
+ void readFromProcess();
+ void exitLoopSlot();
+ void restartProcess();
+ void waitForReadyReadInAReadyReadSlotSlot();
+ void waitForBytesWrittenInABytesWrittenSlotSlot();
+
+private:
+ QProcess *process;
+ qint64 bytesAvailable;
+};
+
+// Testing get/set functions
+void tst_QProcess::getSetCheck()
+{
+ QProcess obj1;
+ // ProcessChannelMode QProcess::readChannelMode()
+ // void QProcess::setReadChannelMode(ProcessChannelMode)
+ obj1.setReadChannelMode(QProcess::ProcessChannelMode(QProcess::SeparateChannels));
+ QCOMPARE(QProcess::ProcessChannelMode(QProcess::SeparateChannels), obj1.readChannelMode());
+ obj1.setReadChannelMode(QProcess::ProcessChannelMode(QProcess::MergedChannels));
+ QCOMPARE(QProcess::ProcessChannelMode(QProcess::MergedChannels), obj1.readChannelMode());
+ obj1.setReadChannelMode(QProcess::ProcessChannelMode(QProcess::ForwardedChannels));
+ QCOMPARE(QProcess::ProcessChannelMode(QProcess::ForwardedChannels), obj1.readChannelMode());
+
+ // ProcessChannel QProcess::readChannel()
+ // void QProcess::setReadChannel(ProcessChannel)
+ obj1.setReadChannel(QProcess::ProcessChannel(QProcess::StandardOutput));
+ QCOMPARE(QProcess::ProcessChannel(QProcess::StandardOutput), obj1.readChannel());
+ obj1.setReadChannel(QProcess::ProcessChannel(QProcess::StandardError));
+ QCOMPARE(QProcess::ProcessChannel(QProcess::StandardError), obj1.readChannel());
+}
+
+tst_QProcess::tst_QProcess()
+{
+}
+
+tst_QProcess::~tst_QProcess()
+{
+}
+
+void tst_QProcess::init()
+{
+#ifdef Q_OS_SYMBIAN
+ QString dirStr = QString::fromLatin1("c:\\logs");
+ QDir dir;
+ if (!dir.exists(dirStr))
+ dir.mkpath(dirStr);
+#endif
+}
+
+void tst_QProcess::cleanup()
+{
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::constructing()
+{
+ QProcess process;
+ QCOMPARE(process.readChannel(), QProcess::StandardOutput);
+ QCOMPARE(process.workingDirectory(), QString());
+ QCOMPARE(process.environment(), QStringList());
+ QCOMPARE(process.error(), QProcess::UnknownError);
+ QCOMPARE(process.state(), QProcess::NotRunning);
+ QCOMPARE(process.pid(), Q_PID(0));
+ QCOMPARE(process.readAllStandardOutput(), QByteArray());
+ QCOMPARE(process.readAllStandardError(), QByteArray());
+ QCOMPARE(process.canReadLine(), false);
+
+ // QIODevice
+ QCOMPARE(process.openMode(), QIODevice::NotOpen);
+ QVERIFY(!process.isOpen());
+ QVERIFY(!process.isReadable());
+ QVERIFY(!process.isWritable());
+ QVERIFY(process.isSequential());
+ QCOMPARE(process.pos(), qlonglong(0));
+ QCOMPARE(process.size(), qlonglong(0));
+ QVERIFY(process.atEnd());
+ QCOMPARE(process.bytesAvailable(), qlonglong(0));
+ QCOMPARE(process.bytesToWrite(), qlonglong(0));
+ QVERIFY(!process.errorString().isEmpty());
+
+ char c;
+ QCOMPARE(process.read(&c, 1), qlonglong(-1));
+ QCOMPARE(process.write(&c, 1), qlonglong(-1));
+
+ QProcess proc2;
+}
+
+void tst_QProcess::simpleStart()
+{
+ qRegisterMetaType<QProcess::ProcessState>("QProcess::ProcessState");
+
+ process = new QProcess;
+ QSignalSpy spy(process, SIGNAL(stateChanged(QProcess::ProcessState)));
+ connect(process, SIGNAL(readyRead()), this, SLOT(readFromProcess()));
+
+ /* valgrind dislike SUID binaries(those that have the `s'-flag set), which
+ * makes it fail to start the process. For this reason utilities like `ping' won't
+ * start, when the auto test is run through `valgrind'. */
+ process->start("testProcessNormal/testProcessNormal");
+ if (process->state() != QProcess::Starting)
+ QCOMPARE(process->state(), QProcess::Running);
+ QVERIFY2(process->waitForStarted(5000), qPrintable(process->errorString()));
+ QCOMPARE(process->state(), QProcess::Running);
+#if defined(Q_OS_WINCE)
+ // Note: This actually seems incorrect, it will only exit the while loop when finishing fails
+ while (process->waitForFinished(5000))
+ { }
+#elif defined(Q_OS_SYMBIAN)
+ QVERIFY(process->waitForFinished(5000));
+#else
+ while (process->waitForReadyRead(5000))
+ { }
+#endif
+ QCOMPARE(int(process->state()), int(QProcess::NotRunning));
+
+ delete process;
+ process = 0;
+
+ QCOMPARE(spy.count(), 3);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(spy.at(0).at(0)), QProcess::Starting);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(spy.at(1).at(0)), QProcess::Running);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(spy.at(2).at(0)), QProcess::NotRunning);
+}
+//-----------------------------------------------------------------------------
+void tst_QProcess::execute()
+{
+ QCOMPARE(QProcess::execute("testProcessNormal/testProcessNormal",
+ QStringList() << "arg1" << "arg2"), 0);
+ QCOMPARE(QProcess::execute("nonexistingexe"), -2);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::startDetached()
+{
+ QProcess proc;
+ QVERIFY(proc.startDetached("testProcessNormal/testProcessNormal",
+ QStringList() << "arg1" << "arg2"));
+ QCOMPARE(QProcess::startDetached("nonexistingexe"), false);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::readFromProcess()
+{
+ int lines = 0;
+ while (process->canReadLine()) {
+ ++lines;
+ QByteArray line = process->readLine();
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::crashTest()
+{
+ qRegisterMetaType<QProcess::ProcessState>("QProcess::ProcessState");
+#ifdef Q_OS_WIN
+ QSKIP("This test opens a crash dialog on Windows", SkipSingle);
+#endif
+ process = new QProcess;
+ QSignalSpy stateSpy(process, SIGNAL(stateChanged(QProcess::ProcessState)));
+ process->start("testProcessCrash/testProcessCrash");
+ QVERIFY(process->waitForStarted(5000));
+
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ExitStatus");
+
+ QSignalSpy spy(process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy spy2(process, SIGNAL(finished(int, QProcess::ExitStatus)));
+
+ QVERIFY(process->waitForFinished(5000));
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed);
+
+ QCOMPARE(spy2.count(), 1);
+ QCOMPARE(*static_cast<const QProcess::ExitStatus *>(spy2.at(0).at(1).constData()), QProcess::CrashExit);
+
+ QCOMPARE(process->exitStatus(), QProcess::CrashExit);
+
+ delete process;
+ process = 0;
+
+ QCOMPARE(stateSpy.count(), 3);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(stateSpy.at(0).at(0)), QProcess::Starting);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(stateSpy.at(1).at(0)), QProcess::Running);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(stateSpy.at(2).at(0)), QProcess::NotRunning);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::crashTest2()
+{
+#ifdef Q_OS_WIN
+ QSKIP("This test opens a crash dialog on Windows", SkipSingle);
+#endif
+ process = new QProcess;
+ process->start("testProcessCrash/testProcessCrash");
+ QVERIFY(process->waitForStarted(5000));
+
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ExitStatus");
+
+ QSignalSpy spy(process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy spy2(process, SIGNAL(finished(int, QProcess::ExitStatus)));
+
+ QObject::connect(process, SIGNAL(finished(int)), this, SLOT(exitLoopSlot()));
+
+ QTestEventLoop::instance().enterLoop(5);
+ if (QTestEventLoop::instance().timeout())
+ QFAIL("Failed to detect crash : operation timed out");
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed);
+
+ QCOMPARE(spy2.count(), 1);
+ QCOMPARE(*static_cast<const QProcess::ExitStatus *>(spy2.at(0).at(1).constData()), QProcess::CrashExit);
+
+ QCOMPARE(process->exitStatus(), QProcess::CrashExit);
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::echoTest_data()
+{
+ QTest::addColumn<QByteArray>("input");
+
+ QTest::newRow("1") << QByteArray("H");
+ QTest::newRow("2") << QByteArray("He");
+ QTest::newRow("3") << QByteArray("Hel");
+ QTest::newRow("4") << QByteArray("Hell");
+ QTest::newRow("5") << QByteArray("Hello");
+ QTest::newRow("100 bytes") << QByteArray(100, '@');
+ QTest::newRow("1000 bytes") << QByteArray(1000, '@');
+ QTest::newRow("10000 bytes") << QByteArray(10000, '@');
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::echoTest()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QFETCH(QByteArray, input);
+
+ process = new QProcess;
+ connect(process, SIGNAL(readyRead()), this, SLOT(exitLoopSlot()));
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+ QVERIFY(process->waitForStarted(5000));
+
+ process->write(input);
+
+ QTime stopWatch;
+ stopWatch.start();
+ do {
+ QVERIFY(process->isOpen());
+ QTestEventLoop::instance().enterLoop(2);
+ } while (stopWatch.elapsed() < 60000 && process->bytesAvailable() < input.size());
+ if (stopWatch.elapsed() >= 60000)
+ QFAIL("Timed out");
+
+ QByteArray message = process->readAll();
+ QCOMPARE(message.size(), input.size());
+
+ char *c1 = message.data();
+ char *c2 = input.data();
+ while (*c1 && *c2) {
+ if (*c1 != *c2)
+ QCOMPARE(*c1, *c2);
+ ++c1;
+ ++c2;
+ }
+ QCOMPARE(*c1, *c2);
+
+ process->write("", 1);
+
+ QVERIFY(process->waitForFinished(5000));
+
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::exitLoopSlot()
+{
+ QTestEventLoop::instance().exitLoop();
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::echoTest2()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process = new QProcess;
+ connect(process, SIGNAL(readyRead()), this, SLOT(exitLoopSlot()));
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho2/testProcessEcho2.app");
+#else
+ process->start("testProcessEcho2/testProcessEcho2");
+#endif
+ QVERIFY(process->waitForStarted(5000));
+ QVERIFY(!process->waitForReadyRead(250));
+ QCOMPARE(process->error(), QProcess::Timedout);
+
+ process->write("Hello");
+ QSignalSpy spy1(process, SIGNAL(readyReadStandardOutput()));
+ QSignalSpy spy2(process, SIGNAL(readyReadStandardError()));
+
+ QTime stopWatch;
+ stopWatch.start();
+ forever {
+ QTestEventLoop::instance().enterLoop(1);
+ if (stopWatch.elapsed() >= 30000)
+ QFAIL("Timed out");
+ process->setReadChannel(QProcess::StandardOutput);
+ qint64 baso = process->bytesAvailable();
+
+ process->setReadChannel(QProcess::StandardError);
+ qint64 base = process->bytesAvailable();
+ if (baso == 5 && base == 5)
+ break;
+ }
+
+ QVERIFY(spy1.count() > 0);
+ QVERIFY(spy2.count() > 0);
+
+ QCOMPARE(process->readAllStandardOutput(), QByteArray("Hello"));
+ QCOMPARE(process->readAllStandardError(), QByteArray("Hello"));
+
+ process->write("", 1);
+ QVERIFY(process->waitForFinished(5000));
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::echoTest_performance()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+#ifdef Q_OS_MAC
+ process.start("testProcessLoopback/testProcessLoopback.app");
+#else
+ process.start("testProcessLoopback/testProcessLoopback");
+#endif
+
+ QByteArray array;
+ array.resize(1024 * 1024);
+ for (int j = 0; j < array.size(); ++j)
+ array[j] = 'a' + (j % 20);
+
+ QVERIFY(process.waitForStarted());
+
+ QTime stopWatch;
+ stopWatch.start();
+
+ qint64 totalBytes = 0;
+ QByteArray dump;
+ QSignalSpy readyReadSpy(&process, SIGNAL(readyRead()));
+ while (stopWatch.elapsed() < 2000) {
+ process.write(array);
+ while (process.bytesToWrite() > 0) {
+ int readCount = readyReadSpy.count();
+ QVERIFY(process.waitForBytesWritten(5000));
+ if (readyReadSpy.count() == readCount)
+ QVERIFY(process.waitForReadyRead(5000));
+ }
+
+ while (process.bytesAvailable() < array.size())
+ QVERIFY2(process.waitForReadyRead(5000), qPrintable(process.errorString()));
+ dump = process.readAll();
+ totalBytes += dump.size();
+ }
+
+ qDebug() << "Elapsed time:" << stopWatch.elapsed() << "ms;"
+ << "transfer rate:" << totalBytes / (1048.576) / stopWatch.elapsed()
+ << "MB/s";
+
+ for (int j = 0; j < array.size(); ++j)
+ QCOMPARE(char(dump.at(j)), char('a' + (j % 20)));
+
+ process.closeWriteChannel();
+ QVERIFY(process.waitForFinished());
+}
+
+#if defined Q_OS_WIN
+//-----------------------------------------------------------------------------
+void tst_QProcess::echoTestGui()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+
+ process.start("testProcessEchoGui/testProcessEchoGui");
+
+
+ process.write("Hello");
+ process.write("q");
+
+ QVERIFY(process.waitForFinished(50000));
+
+ QCOMPARE(process.readAllStandardOutput(), QByteArray("Hello"));
+ QCOMPARE(process.readAllStandardError(), QByteArray("Hello"));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::batFiles_data()
+{
+ QTest::addColumn<QString>("batFile");
+ QTest::addColumn<QByteArray>("output");
+
+ QTest::newRow("simple") << QString::fromLatin1("testBatFiles/simple.bat") << QByteArray("Hello");
+ QTest::newRow("with space") << QString::fromLatin1("testBatFiles/with space.bat") << QByteArray("Hello");
+}
+
+void tst_QProcess::batFiles()
+{
+#if defined(Q_OS_WINCE)
+ QSKIP("Batch files are not supported on Windows CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Batch files are not supported on Symbian", SkipAll);
+#endif
+ QFETCH(QString, batFile);
+ QFETCH(QByteArray, output);
+
+ QProcess proc;
+
+ proc.start(batFile, QStringList());
+
+ QVERIFY(proc.waitForFinished(5000));
+
+ QVERIFY(proc.bytesAvailable() > 0);
+
+ QVERIFY(proc.readAll().startsWith(output));
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::exitStatus_data()
+{
+ QTest::addColumn<QStringList>("processList");
+ QTest::addColumn<QList<QProcess::ExitStatus> >("exitStatus");
+
+ QTest::newRow("normal") << (QStringList() << "testProcessNormal/testProcessNormal")
+ << (QList<QProcess::ExitStatus>() << QProcess::NormalExit);
+ QTest::newRow("crash") << (QStringList() << "testProcessCrash/testProcessCrash")
+ << (QList<QProcess::ExitStatus>() << QProcess::CrashExit);
+
+ QTest::newRow("normal-crash") << (QStringList()
+ << "testProcessNormal/testProcessNormal"
+ << "testProcessCrash/testProcessCrash")
+ << (QList<QProcess::ExitStatus>()
+ << QProcess::NormalExit
+ << QProcess::CrashExit);
+ QTest::newRow("crash-normal") << (QStringList()
+ << "testProcessCrash/testProcessCrash"
+ << "testProcessNormal/testProcessNormal")
+ << (QList<QProcess::ExitStatus>()
+ << QProcess::CrashExit
+ << QProcess::NormalExit);
+}
+
+void tst_QProcess::exitStatus()
+{
+ process = new QProcess;
+ QFETCH(QStringList, processList);
+ QFETCH(QList<QProcess::ExitStatus>, exitStatus);
+
+#ifdef Q_OS_WIN
+ if (exitStatus.contains(QProcess::CrashExit))
+ QSKIP("This test opens a crash dialog on Windows", SkipSingle);
+#endif
+
+ Q_ASSERT(processList.count() == exitStatus.count());
+ for (int i = 0; i < processList.count(); ++i) {
+ process->start(processList.at(i));
+ QVERIFY(process->waitForStarted(5000));
+ QVERIFY(process->waitForFinished(5000));
+
+ QCOMPARE(process->exitStatus(), exitStatus.at(i));
+ }
+
+ process->deleteLater();
+ process = 0;
+}
+//-----------------------------------------------------------------------------
+void tst_QProcess::loopBackTest()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process = new QProcess;
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+ QVERIFY(process->waitForStarted(5000));
+
+ for (int i = 0; i < 100; ++i) {
+ process->write("Hello");
+ do {
+ QVERIFY(process->waitForReadyRead(5000));
+ } while (process->bytesAvailable() < 5);
+ QCOMPARE(process->readAll(), QByteArray("Hello"));
+ }
+
+ process->write("", 1);
+ QVERIFY(process->waitForFinished(5000));
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::readTimeoutAndThenCrash()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process = new QProcess;
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+ if (process->state() != QProcess::Starting)
+ QCOMPARE(process->state(), QProcess::Running);
+
+ QVERIFY(process->waitForStarted(5000));
+ QCOMPARE(process->state(), QProcess::Running);
+
+ QVERIFY(!process->waitForReadyRead(5000));
+ QCOMPARE(process->error(), QProcess::Timedout);
+
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ QSignalSpy spy(process, SIGNAL(error(QProcess::ProcessError)));
+
+ process->kill();
+
+ QVERIFY(process->waitForFinished(5000));
+ QCOMPARE(process->state(), QProcess::NotRunning);
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(*static_cast<const QProcess::ProcessError *>(spy.at(0).at(0).constData()), QProcess::Crashed);
+
+ delete process;
+ process = 0;
+}
+
+void tst_QProcess::waitForFinished()
+{
+ QProcess process;
+
+#ifdef Q_OS_MAC
+ process.start("testProcessOutput/testProcessOutput.app");
+#else
+ process.start("testProcessOutput/testProcessOutput");
+#endif
+
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QVERIFY(process.waitForFinished(5000));
+#else
+ QVERIFY(process.waitForFinished(30000));
+#endif
+ QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+
+#if defined(Q_OS_SYMBIAN)
+ // Symbian test outputs to a file, so check that
+ FILE* file = fopen("c:\\logs\\qprocess_output_test.txt","r");
+ int retval = 0;
+ int count = 0;
+ while((int)(retval = fgetc(file) )!= EOF)
+ if (retval == '\n')
+ count++;
+ fclose(file);
+ QCOMPARE(count, 200);
+#else
+# if defined (Q_OS_WINCE)
+ QEXPECT_FAIL("", "Reading and writing to a process is not supported on Qt/CE", Continue);
+# endif
+ QString output = process.readAll();
+ QCOMPARE(output.count("\n"), 10*1024);
+#endif
+
+ process.start("blurdybloop");
+ QVERIFY(!process.waitForFinished());
+ QCOMPARE(process.error(), QProcess::FailedToStart);
+}
+
+
+void tst_QProcess::deadWhileReading()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+
+#ifdef Q_OS_MAC
+ process.start("testProcessDeadWhileReading/testProcessDeadWhileReading.app");
+#else
+ process.start("testProcessDeadWhileReading/testProcessDeadWhileReading");
+#endif
+
+ QString output;
+
+ QVERIFY(process.waitForStarted(5000));
+ while (process.waitForReadyRead(5000))
+ output += process.readAll();
+
+ QCOMPARE(output.count("\n"), 10*1024);
+ process.waitForFinished();
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::restartProcessDeadlock()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ // The purpose of this test is to detect whether restarting a
+ // process in the finished() connected slot causes a deadlock
+ // because of the way QProcessManager uses its locks.
+ QProcess proc;
+ process = &proc;
+ connect(process, SIGNAL(finished(int)), this, SLOT(restartProcess()));
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+
+ QCOMPARE(process->write("", 1), qlonglong(1));
+ QVERIFY(process->waitForFinished(5000));
+
+ process->disconnect(SIGNAL(finished(int)));
+
+ QCOMPARE(process->write("", 1), qlonglong(1));
+ QVERIFY(process->waitForFinished(5000));
+}
+
+void tst_QProcess::restartProcess()
+{
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::closeWriteChannel()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess more;
+ more.start("testProcessEOF/testProcessEOF");
+
+ QVERIFY(more.waitForStarted(5000));
+ QVERIFY(!more.waitForReadyRead(250));
+ QCOMPARE(more.error(), QProcess::Timedout);
+
+ QVERIFY(more.write("Data to read") != -1);
+
+ QVERIFY(!more.waitForReadyRead(250));
+ QCOMPARE(more.error(), QProcess::Timedout);
+
+ more.closeWriteChannel();
+
+ QVERIFY(more.waitForReadyRead(5000));
+ QVERIFY(more.readAll().startsWith("Data to read"));
+
+ if (more.state() == QProcess::Running)
+ more.write("q");
+ QVERIFY(more.waitForFinished(5000));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::closeReadChannel()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ for (int i = 0; i < 10; ++i) {
+ QProcess::ProcessChannel channel1 = QProcess::StandardOutput;
+ QProcess::ProcessChannel channel2 = QProcess::StandardError;
+
+ QProcess proc;
+#ifdef Q_OS_MAC
+ proc.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ proc.start("testProcessEcho2/testProcessEcho2");
+#endif
+ QVERIFY(proc.waitForStarted(5000));
+ proc.closeReadChannel(i&1 ? channel2 : channel1);
+ proc.setReadChannel(i&1 ? channel2 : channel1);
+ proc.write("Data");
+
+ QVERIFY(!proc.waitForReadyRead(5000));
+ QVERIFY(proc.readAll().isEmpty());
+
+ proc.setReadChannel(i&1 ? channel1 : channel2);
+
+ while (proc.bytesAvailable() < 4 && proc.waitForReadyRead(5000))
+ { }
+
+ QCOMPARE(proc.readAll(), QByteArray("Data"));
+
+ proc.write("", 1);
+ QVERIFY(proc.waitForFinished(5000));
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::openModes()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess proc;
+ QVERIFY(!proc.isOpen());
+ QVERIFY(proc.openMode() == QProcess::NotOpen);
+#ifdef Q_OS_MAC
+ proc.start("testProcessEcho3/testProcessEcho3.app");
+#else
+ proc.start("testProcessEcho3/testProcessEcho3");
+#endif
+ QVERIFY(proc.waitForStarted(5000));
+ QVERIFY(proc.isOpen());
+ QVERIFY(proc.openMode() == QProcess::ReadWrite);
+ QVERIFY(proc.isReadable());
+ QVERIFY(proc.isWritable());
+
+ proc.write("Data");
+
+ proc.closeWriteChannel();
+
+ QVERIFY(proc.isWritable());
+ QVERIFY(proc.openMode() == QProcess::ReadWrite);
+
+ while (proc.bytesAvailable() < 4 && proc.waitForReadyRead(5000))
+ { }
+
+ QCOMPARE(proc.readAll().constData(), QByteArray("Data").constData());
+
+ proc.closeReadChannel(QProcess::StandardOutput);
+
+ QVERIFY(proc.openMode() == QProcess::ReadWrite);
+ QVERIFY(proc.isReadable());
+
+ proc.closeReadChannel(QProcess::StandardError);
+
+ QVERIFY(proc.openMode() == QProcess::ReadWrite);
+ QVERIFY(proc.isReadable());
+
+ proc.close();
+ QVERIFY(!proc.isOpen());
+ QVERIFY(!proc.isReadable());
+ QVERIFY(!proc.isWritable());
+ QCOMPARE(proc.state(), QProcess::NotRunning);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::emitReadyReadOnlyWhenNewDataArrives()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess proc;
+ connect(&proc, SIGNAL(readyRead()), this, SLOT(exitLoopSlot()));
+ QSignalSpy spy(&proc, SIGNAL(readyRead()));
+
+#ifdef Q_OS_MAC
+ proc.start("testProcessEcho/testProcessEcho.app");
+#else
+ proc.start("testProcessEcho/testProcessEcho");
+#endif
+
+ QCOMPARE(spy.count(), 0);
+
+ proc.write("A");
+
+ QTestEventLoop::instance().enterLoop(5);
+ if (QTestEventLoop::instance().timeout())
+ QFAIL("Operation timed out");
+
+ QCOMPARE(spy.count(), 1);
+
+ QTestEventLoop::instance().enterLoop(1);
+ QVERIFY(QTestEventLoop::instance().timeout());
+ QVERIFY(!proc.waitForReadyRead(250));
+
+ QObject::disconnect(&proc, SIGNAL(readyRead()), 0, 0);
+ proc.write("B");
+ QVERIFY(proc.waitForReadyRead(5000));
+
+ proc.write("", 1);
+ QVERIFY(proc.waitForFinished(5000));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::hardExit()
+{
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Killing started processes is not supported on Qt/Symbian due platform security", SkipAll);
+#endif
+ QProcess proc;
+
+#if defined(Q_OS_MAC)
+ proc.start("testProcessEcho/testProcessEcho.app");
+#elif defined(Q_OS_WINCE)
+ proc.start("testSoftExit/testSoftExit");
+#else
+ proc.start("testProcessEcho/testProcessEcho");
+#endif
+
+#ifndef Q_OS_WINCE
+ QVERIFY(proc.waitForStarted(5000));
+#else
+ QVERIFY(proc.waitForStarted(10000));
+#endif
+
+ proc.kill();
+
+ QVERIFY(proc.waitForFinished(5000));
+ QCOMPARE(int(proc.state()), int(QProcess::NotRunning));
+ QCOMPARE(int(proc.error()), int(QProcess::Crashed));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::softExit()
+{
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Terminating started processes is not supported on Qt/Symbian due platform security", SkipAll);
+#endif
+ QProcess proc;
+
+ proc.start("testSoftExit/testSoftExit");
+
+ QVERIFY(proc.waitForStarted(10000));
+#if !defined(Q_OS_WINCE)
+ QVERIFY(proc.waitForReadyRead(10000));
+#endif
+
+ proc.terminate();
+
+ QVERIFY(proc.waitForFinished(10000));
+ QCOMPARE(int(proc.state()), int(QProcess::NotRunning));
+ QCOMPARE(int(proc.error()), int(QProcess::UnknownError));
+}
+
+class SoftExitProcess : public QProcess
+{
+ Q_OBJECT
+public:
+ bool waitedForFinished;
+
+ SoftExitProcess(int n) : waitedForFinished(false), n(n), killing(false)
+ {
+ connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
+ this, SLOT(finishedSlot(int, QProcess::ExitStatus)));
+
+ switch (n) {
+ case 0:
+ setReadChannelMode(QProcess::MergedChannels);
+ connect(this, SIGNAL(readyRead()), this, SLOT(terminateSlot()));
+ break;
+ case 1:
+ connect(this, SIGNAL(readyReadStandardOutput()),
+ this, SLOT(terminateSlot()));
+ break;
+ case 2:
+ connect(this, SIGNAL(readyReadStandardError()),
+ this, SLOT(terminateSlot()));
+ break;
+ case 3:
+ connect(this, SIGNAL(started()),
+ this, SLOT(terminateSlot()));
+ break;
+ case 4:
+ default:
+ connect(this, SIGNAL(stateChanged(QProcess::ProcessState)),
+ this, SLOT(terminateSlot()));
+ break;
+ }
+ }
+
+public slots:
+ void terminateSlot()
+ {
+ if (killing || (n == 4 && state() != Running)) {
+ // Don't try to kill the process before it is running - that can
+ // be hazardous, as the actual child process might not be running
+ // yet. Also, don't kill it "recursively".
+ return;
+ }
+ killing = true;
+ readAll();
+ terminate();
+ if ((waitedForFinished = waitForFinished(5000)) == false) {
+ kill();
+ if (state() != NotRunning)
+ waitedForFinished = waitForFinished(5000);
+ }
+ }
+
+ void finishedSlot(int, QProcess::ExitStatus)
+ {
+ waitedForFinished = true;
+ }
+
+private:
+ int n;
+ bool killing;
+};
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::softExitInSlots_data()
+{
+ QTest::addColumn<QString>("appName");
+
+#ifdef Q_OS_MAC
+ QTest::newRow("gui app") << "testGuiProcess/testGuiProcess.app";
+#else
+ QTest::newRow("gui app") << "testGuiProcess/testGuiProcess";
+#endif
+#ifdef Q_OS_MAC
+ QTest::newRow("console app") << "testProcessEcho2/testProcessEcho2.app";
+#else
+ QTest::newRow("console app") << "testProcessEcho2/testProcessEcho2";
+#endif
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::softExitInSlots()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QFETCH(QString, appName);
+
+ for (int i = 0; i < 5; ++i) {
+ SoftExitProcess proc(i);
+ proc.start(appName);
+ proc.write("OLEBOLE", 8); // include the \0
+ QTestEventLoop::instance().enterLoop(10);
+ QCOMPARE(proc.state(), QProcess::NotRunning);
+ QVERIFY(proc.waitedForFinished);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::mergedChannels()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+ process.setReadChannelMode(QProcess::MergedChannels);
+ QCOMPARE(process.readChannelMode(), QProcess::MergedChannels);
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ process.start("testProcessEcho2/testProcessEcho2");
+#endif
+
+ QVERIFY(process.waitForStarted(5000));
+
+ for (int i = 0; i < 100; ++i) {
+ QCOMPARE(process.write("abc"), qlonglong(3));
+ while (process.bytesAvailable() < 6)
+ QVERIFY(process.waitForReadyRead(5000));
+ QCOMPARE(process.readAll(), QByteArray("aabbcc"));
+ }
+
+ process.closeWriteChannel();
+ QVERIFY(process.waitForFinished(5000));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::forwardedChannels()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+ process.setReadChannelMode(QProcess::ForwardedChannels);
+ QCOMPARE(process.readChannelMode(), QProcess::ForwardedChannels);
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ process.start("testProcessEcho2/testProcessEcho2");
+#endif
+
+ QVERIFY(process.waitForStarted(5000));
+ QCOMPARE(process.write("forwarded\n"), qlonglong(10));
+ QVERIFY(!process.waitForReadyRead(250));
+ QCOMPARE(process.bytesAvailable(), qlonglong(0));
+
+ process.closeWriteChannel();
+ QVERIFY(process.waitForFinished(5000));
+}
+
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::atEnd()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho/testProcessEcho.app");
+#else
+ process.start("testProcessEcho/testProcessEcho");
+#endif
+ process.write("abcdefgh\n");
+
+ while (process.bytesAvailable() < 8)
+ QVERIFY(process.waitForReadyRead(5000));
+
+ QTextStream stream(&process);
+ QVERIFY(!stream.atEnd());
+ QString tmp = stream.readLine();
+ QVERIFY(stream.atEnd());
+ QCOMPARE(tmp, QString::fromLatin1("abcdefgh"));
+
+ process.write("", 1);
+ QVERIFY(process.waitForFinished(5000));
+}
+
+class TestThread : public QThread
+{
+ Q_OBJECT
+public:
+ inline int code()
+ {
+ return exitCode;
+ }
+
+#if defined(Q_OS_SYMBIAN)
+ int serial;
+#endif
+
+protected:
+ inline void run()
+ {
+ exitCode = 90210;
+
+ QProcess process;
+ connect(&process, SIGNAL(finished(int)), this, SLOT(catchExitCode(int)),
+ Qt::DirectConnection);
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho/testProcessEcho.app");
+#elif defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ // WINSCW builds in Symbian do not allow multiple processes to load Qt libraries,
+ // so use just a simple process instead of testDetached.
+ process.start("testProcessNormal");
+#elif defined(Q_OS_SYMBIAN)
+ // testDetached used because it does something, but doesn't take too long.
+ QFile infoFile(QString("c:\\logs\\detinfo%1").arg(serial));
+ QStringList args;
+ args << infoFile.fileName();
+ process.start("testDetached", args);
+#else
+ process.start("testProcessEcho/testProcessEcho");
+#endif
+
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QCOMPARE(process.write("abc\0", 4), qint64(4));
+#endif
+ exitCode = exec();
+ }
+
+protected slots:
+ inline void catchExitCode(int exitCode)
+ {
+ this->exitCode = exitCode;
+ exit(exitCode);
+ }
+
+private:
+ int exitCode;
+};
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::processInAThread()
+{
+ for (int i = 0; i < 10; ++i) {
+ TestThread thread;
+ thread.start();
+ QVERIFY(thread.wait(10000));
+ QCOMPARE(thread.code(), 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::processesInMultipleThreads()
+{
+#if defined(Q_OS_SYMBIAN)
+ int serialCounter = 0;
+#endif
+
+ for (int i = 0; i < 10; ++i) {
+ TestThread thread1;
+ TestThread thread2;
+ TestThread thread3;
+
+#if defined(Q_OS_SYMBIAN)
+ thread1.serial = serialCounter++;
+ thread2.serial = serialCounter++;
+ thread3.serial = serialCounter++;
+#endif
+ thread1.start();
+ thread2.start();
+ thread3.start();
+
+ QVERIFY(thread2.wait(10000));
+ QVERIFY(thread3.wait(10000));
+ QVERIFY(thread1.wait(10000));
+
+ QCOMPARE(thread1.code(), 0);
+ QCOMPARE(thread2.code(), 0);
+ QCOMPARE(thread3.code(), 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForFinishedWithTimeout()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+
+ process = new QProcess(this);
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#elif defined(Q_OS_SYMBIAN)
+ process->start("testProcessOutput");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ QVERIFY(process->waitForStarted(50));
+ QVERIFY(!process->waitForFinished(1));
+#else
+ QVERIFY(process->waitForStarted(5000));
+ QVERIFY(!process->waitForFinished(1));
+
+ process->write("", 1);
+#endif
+
+ QVERIFY(process->waitForFinished());
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForReadyReadInAReadyReadSlot()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process = new QProcess(this);
+ connect(process, SIGNAL(readyRead()), this, SLOT(waitForReadyReadInAReadyReadSlotSlot()));
+ connect(process, SIGNAL(finished(int)), this, SLOT(exitLoopSlot()));
+ bytesAvailable = 0;
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+ QVERIFY(process->waitForStarted(5000));
+
+ QSignalSpy spy(process, SIGNAL(readyRead()));
+ process->write("foo");
+ QTestEventLoop::instance().enterLoop(30);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(spy.count(), 1);
+
+ process->disconnect();
+ QVERIFY(process->waitForFinished(5000));
+ QVERIFY(process->bytesAvailable() > bytesAvailable);
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForReadyReadInAReadyReadSlotSlot()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ bytesAvailable = process->bytesAvailable();
+ process->write("bar", 4);
+ QVERIFY(process->waitForReadyRead(5000));
+ QTestEventLoop::instance().exitLoop();
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForBytesWrittenInABytesWrittenSlot()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process = new QProcess(this);
+ connect(process, SIGNAL(bytesWritten(qint64)), this, SLOT(waitForBytesWrittenInABytesWrittenSlotSlot()));
+ bytesAvailable = 0;
+
+#ifdef Q_OS_MAC
+ process->start("testProcessEcho/testProcessEcho.app");
+#else
+ process->start("testProcessEcho/testProcessEcho");
+#endif
+ QVERIFY(process->waitForStarted(5000));
+
+ qRegisterMetaType<qint64>("qint64");
+ QSignalSpy spy(process, SIGNAL(bytesWritten(qint64)));
+ process->write("f");
+ QTestEventLoop::instance().enterLoop(30);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(spy.count(), 1);
+ process->write("", 1);
+ process->disconnect();
+ QVERIFY(process->waitForFinished());
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForBytesWrittenInABytesWrittenSlotSlot()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ process->write("b");
+ QVERIFY(process->waitForBytesWritten(5000));
+ QTestEventLoop::instance().exitLoop();
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::spaceArgsTest_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QString>("stringArgs");
+
+ // arg1 | arg2
+ QTest::newRow("arg1 arg2") << (QStringList() << QString::fromLatin1("arg1") << QString::fromLatin1("arg2"))
+ << QString::fromLatin1("arg1 arg2");
+ // "arg1" | ar "g2
+ QTest::newRow("\"\"\"\"arg1\"\"\"\" \"ar \"\"\"g2\"") << (QStringList() << QString::fromLatin1("\"arg1\"") << QString::fromLatin1("ar \"g2"))
+ << QString::fromLatin1("\"\"\"\"arg1\"\"\"\" \"ar \"\"\"g2\"");
+ // ar g1 | a rg 2
+ QTest::newRow("\"ar g1\" \"a rg 2\"") << (QStringList() << QString::fromLatin1("ar g1") << QString::fromLatin1("a rg 2"))
+ << QString::fromLatin1("\"ar g1\" \"a rg 2\"");
+ // -lar g1 | -l"ar g2"
+ QTest::newRow("\"-lar g1\" \"-l\"\"\"ar g2\"\"\"\"") << (QStringList() << QString::fromLatin1("-lar g1") << QString::fromLatin1("-l\"ar g2\""))
+ << QString::fromLatin1("\"-lar g1\" \"-l\"\"\"ar g2\"\"\"\"");
+ // ar"g1
+ QTest::newRow("ar\"\"\"\"g1") << (QStringList() << QString::fromLatin1("ar\"g1"))
+ << QString::fromLatin1("ar\"\"\"\"g1");
+ // ar/g1
+ QTest::newRow("ar\\g1") << (QStringList() << QString::fromLatin1("ar\\g1"))
+ << QString::fromLatin1("ar\\g1");
+ // ar\g"1
+ QTest::newRow("ar\\g\"\"\"\"1") << (QStringList() << QString::fromLatin1("ar\\g\"1"))
+ << QString::fromLatin1("ar\\g\"\"\"\"1");
+ // arg\"1
+ QTest::newRow("arg\\\"\"\"1") << (QStringList() << QString::fromLatin1("arg\\\"1"))
+ << QString::fromLatin1("arg\\\"\"\"1");
+ // """"
+ QTest::newRow("\"\"\"\"\"\"\"\"\"\"\"\"") << (QStringList() << QString::fromLatin1("\"\"\"\""))
+ << QString::fromLatin1("\"\"\"\"\"\"\"\"\"\"\"\"");
+ // """" | "" ""
+ QTest::newRow("\"\"\"\"\"\"\"\"\"\"\"\" \"\"\"\"\"\"\" \"\"\"\"\"\"\"") << (QStringList() << QString::fromLatin1("\"\"\"\"") << QString::fromLatin1("\"\" \"\""))
+ << QString::fromLatin1("\"\"\"\"\"\"\"\"\"\"\"\" \"\"\"\"\"\"\" \"\"\"\"\"\"\"");
+ // "" ""
+ QTest::newRow("\"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" (bogus double quotes)") << (QStringList() << QString::fromLatin1("\"\" \"\""))
+ << QString::fromLatin1("\"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\"");
+ // "" ""
+ QTest::newRow(" \"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" (bogus double quotes)") << (QStringList() << QString::fromLatin1("\"\" \"\""))
+ << QString::fromLatin1(" \"\"\"\"\"\"\" \"\" \"\"\"\"\"\"\" ");
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::spaceArgsTest()
+{
+ QFETCH(QStringList, args);
+ QFETCH(QString, stringArgs);
+
+ QStringList programs;
+ programs << QString::fromLatin1("testProcessSpacesArgs/nospace")
+#if defined(Q_OS_SYMBIAN)
+ ; // Symbian toolchain doesn't like exes with spaces in the name
+#else
+ << QString::fromLatin1("testProcessSpacesArgs/one space")
+ << QString::fromLatin1("testProcessSpacesArgs/two space s");
+#endif
+
+ process = new QProcess(this);
+
+ for (int i = 0; i < programs.size(); ++i) {
+ QString program = programs.at(i);
+ process->start(program, args);
+
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QVERIFY(process->waitForStarted(5000));
+ QVERIFY(process->waitForFinished(5000));
+#else
+ QVERIFY(process->waitForStarted(10000));
+ QVERIFY(process->waitForFinished(10000));
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ // Symbian test outputs to a file, so check that
+ FILE* file = fopen("c:\\logs\\qprocess_args_test.txt","r");
+ QVERIFY(file);
+ char buf[256];
+ fgets(buf, 256, file);
+ fclose(file);
+ QStringList actual = QString::fromLatin1(buf).split("|");
+#elif !defined(Q_OS_WINCE)
+ QStringList actual = QString::fromLatin1(process->readAll()).split("|");
+#endif
+#if !defined(Q_OS_WINCE)
+ QVERIFY(!actual.isEmpty());
+ // not interested in the program name, it might be different.
+ actual.removeFirst();
+
+ QCOMPARE(actual, args);
+#endif
+
+ if (program.contains(" "))
+ program = "\"" + program + "\"";
+
+ if (!stringArgs.isEmpty())
+ program += QString::fromLatin1(" ") + stringArgs;
+
+ process->start(program);
+
+ QVERIFY(process->waitForStarted(5000));
+ QVERIFY(process->waitForFinished(5000));
+
+#if defined(Q_OS_SYMBIAN)
+ // Symbian test outputs to a file, so check that
+ file = fopen("c:\\logs\\qprocess_args_test.txt","r");
+ QVERIFY(file);
+ fgets(buf, 256, file);
+ fclose(file);
+ actual = QString::fromLatin1(buf).split("|");
+#elif !defined(Q_OS_WINCE)
+ actual = QString::fromLatin1(process->readAll()).split("|");
+#endif
+#if !defined(Q_OS_WINCE)
+ QVERIFY(!actual.isEmpty());
+ // not interested in the program name, it might be different.
+ actual.removeFirst();
+
+ QCOMPARE(actual, args);
+#endif
+ }
+
+ delete process;
+ process = 0;
+}
+
+#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::nativeArguments()
+{
+ QProcess proc;
+
+ // This doesn't actually need special quoting, so it is pointless to use
+ // native arguments here, but that's not the point of this test.
+ proc.setNativeArguments("hello kitty, \"*\"!");
+
+ proc.start(QString::fromLatin1("testProcessSpacesArgs/nospace"), QStringList());
+
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QVERIFY(proc.waitForStarted(5000));
+ QVERIFY(proc.waitForFinished(5000));
+#else
+ QVERIFY(proc.waitForStarted(10000));
+ QVERIFY(proc.waitForFinished(10000));
+#endif
+
+#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE)
+ // Symbian test outputs to a file, so check that
+# ifdef Q_OS_SYMBIAN
+ FILE* file = fopen("c:\\logs\\qprocess_args_test.txt","r");
+# else
+ FILE* file = fopen("\\temp\\qprocess_args_test.txt","r");
+# endif
+ QVERIFY(file);
+ char buf[256];
+ fgets(buf, 256, file);
+ fclose(file);
+ QStringList actual = QString::fromLatin1(buf).split("|");
+#else
+ QStringList actual = QString::fromLatin1(proc.readAll()).split("|");
+#endif
+ QVERIFY(!actual.isEmpty());
+ // not interested in the program name, it might be different.
+ actual.removeFirst();
+ QStringList expected;
+#if defined(Q_OS_WINCE)
+ expected << "hello" << "kitty," << "\"*\"!"; // Weird, weird ...
+#else
+ expected << "hello" << "kitty," << "*!";
+#endif
+ QCOMPARE(actual, expected);
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::exitCodeTest()
+{
+#if defined(Q_OS_SYMBIAN)
+ // Kernel will run out of process handles on some hw, as there is some
+ // delay before they are recycled, so limit the amount of processes.
+ for (int i = 0; i < 50; ++i) {
+#else
+ for (int i = 0; i < 255; ++i) {
+#endif
+ QProcess process;
+ process.start("testExitCodes/testExitCodes " + QString::number(i));
+ QVERIFY(process.waitForFinished(5000));
+ QCOMPARE(process.exitCode(), i);
+ QCOMPARE(process.error(), QProcess::UnknownError);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::failToStart()
+{
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
+ qRegisterMetaType<QProcess::ProcessState>("QProcess::ProcessState");
+
+ QProcess process;
+ QSignalSpy stateSpy(&process, SIGNAL(stateChanged(QProcess::ProcessState)));
+ QSignalSpy errorSpy(&process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy finishedSpy(&process, SIGNAL(finished(int)));
+ QSignalSpy finishedSpy2(&process, SIGNAL(finished(int, QProcess::ExitStatus)));
+
+// Mac OS X and HP-UX have a really low default process limit (~100), so spawning
+// to many processes here will cause test failures later on.
+#if defined Q_OS_HPUX
+ const int attempts = 15;
+#elif defined Q_OS_MAC
+ const int attempts = 15;
+#else
+ const int attempts = 50;
+#endif
+
+ for (int j = 0; j < 8; ++j) {
+ for (int i = 0; i < attempts; ++i) {
+ QCOMPARE(errorSpy.count(), j * attempts + i);
+ process.start("/blurp");
+
+ switch (j) {
+ case 0:
+ case 1:
+ QVERIFY(!process.waitForStarted());
+ break;
+ case 2:
+ case 3:
+ QVERIFY(!process.waitForFinished());
+ break;
+ case 4:
+ case 5:
+ QVERIFY(!process.waitForReadyRead());
+ break;
+ case 6:
+ case 7:
+ default:
+ QVERIFY(!process.waitForBytesWritten());
+ break;
+ }
+
+ QCOMPARE(process.error(), QProcess::FailedToStart);
+ QCOMPARE(errorSpy.count(), j * attempts + i + 1);
+ QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(finishedSpy2.count(), 0);
+
+ int it = j * attempts + i + 1;
+
+ QCOMPARE(stateSpy.count(), it * 2);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(stateSpy.at(it * 2 - 2).at(0)), QProcess::Starting);
+ QCOMPARE(qVariantValue<QProcess::ProcessState>(stateSpy.at(it * 2 - 1).at(0)), QProcess::NotRunning);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::failToStartWithWait()
+{
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
+
+ QProcess process;
+ QEventLoop loop;
+ QSignalSpy errorSpy(&process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy finishedSpy(&process, SIGNAL(finished(int)));
+ QSignalSpy finishedSpy2(&process, SIGNAL(finished(int, QProcess::ExitStatus)));
+
+ for (int i = 0; i < 50; ++i) {
+ process.start("/blurp", QStringList() << "-v" << "-debug");
+ process.waitForStarted();
+
+ QCOMPARE(process.error(), QProcess::FailedToStart);
+ QCOMPARE(errorSpy.count(), i + 1);
+ QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(finishedSpy2.count(), 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::failToStartWithEventLoop()
+{
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
+
+ QProcess process;
+ QEventLoop loop;
+ QSignalSpy errorSpy(&process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy finishedSpy(&process, SIGNAL(finished(int)));
+ QSignalSpy finishedSpy2(&process, SIGNAL(finished(int, QProcess::ExitStatus)));
+
+ // The error signal may be emitted before start() returns
+ connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()), Qt::QueuedConnection);
+
+
+ for (int i = 0; i < 50; ++i) {
+ process.start("/blurp", QStringList() << "-v" << "-debug");
+
+ loop.exec();
+
+ QCOMPARE(process.error(), QProcess::FailedToStart);
+ QCOMPARE(errorSpy.count(), i + 1);
+ QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(finishedSpy2.count(), 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::removeFileWhileProcessIsRunning()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QFile file("removeFile.txt");
+ QVERIFY(file.open(QFile::WriteOnly));
+
+ QProcess process;
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho/testProcessEcho.app");
+#else
+ process.start("testProcessEcho/testProcessEcho");
+#endif
+
+ QVERIFY(process.waitForStarted(5000));
+
+ QVERIFY(file.remove());
+
+ process.write("", 1);
+ QVERIFY(process.waitForFinished(5000));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setEnvironment_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<QString>("value");
+
+ QTest::newRow("setting-empty") << "tst_QProcess" << "";
+ QTest::newRow("setting") << "tst_QProcess" << "value";
+
+#ifdef Q_OS_WIN
+ QTest::newRow("unsetting") << "PROMPT" << QString();
+ QTest::newRow("overriding") << "PROMPT" << "value";
+#else
+ QTest::newRow("unsetting") << "PATH" << QString();
+ QTest::newRow("overriding") << "PATH" << "value";
+#endif
+}
+
+void tst_QProcess::setEnvironment()
+{
+#if defined (Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+ QSKIP("OS doesn't support environment variables", SkipAll);
+#endif
+
+ // make sure our environment variables are correct
+ QVERIFY(qgetenv("tst_QProcess").isEmpty());
+ QVERIFY(!qgetenv("PATH").isEmpty());
+#ifdef Q_OS_WIN
+ QVERIFY(!qgetenv("PROMPT").isEmpty());
+#endif
+
+ QFETCH(QString, name);
+ QFETCH(QString, value);
+ QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment";
+
+ {
+ QProcess process;
+ QStringList environment = QProcess::systemEnvironment();
+ if (value.isNull()) {
+ int pos;
+ QRegExp rx(name + "=.*");
+#ifdef Q_OS_WIN
+ rx.setCaseSensitivity(Qt::CaseInsensitive);
+#endif
+ while ((pos = environment.indexOf(rx)) != -1)
+ environment.removeAt(pos);
+ } else {
+ environment.append(name + '=' + value);
+ }
+ process.setEnvironment(environment);
+ process.start(executable, QStringList() << name);
+
+ QVERIFY(process.waitForFinished());
+ if (value.isNull())
+ QCOMPARE(process.exitCode(), 1);
+ else if (!value.isEmpty())
+ QCOMPARE(process.exitCode(), 0);
+
+ QCOMPARE(process.readAll(), value.toLocal8Bit());
+ }
+
+ // re-do the test but set the environment twice, to make sure
+ // that the latter addition overrides
+ // this test doesn't make sense in unsetting
+ if (!value.isNull()) {
+ QProcess process;
+ QStringList environment = QProcess::systemEnvironment();
+ environment.prepend(name + "=This is not the right value");
+ environment.append(name + '=' + value);
+ process.setEnvironment(environment);
+ process.start(executable, QStringList() << name);
+
+ QVERIFY(process.waitForFinished());
+ if (!value.isEmpty())
+ QCOMPARE(process.exitCode(), 0);
+
+ QCOMPARE(process.readAll(), value.toLocal8Bit());
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setProcessEnvironment_data()
+{
+ setEnvironment_data();
+}
+
+void tst_QProcess::setProcessEnvironment()
+{
+#if defined (Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+ QSKIP("OS doesn't support environment variables", SkipAll);
+#endif
+
+ // make sure our environment variables are correct
+ QVERIFY(qgetenv("tst_QProcess").isEmpty());
+ QVERIFY(!qgetenv("PATH").isEmpty());
+#ifdef Q_OS_WIN
+ QVERIFY(!qgetenv("PROMPT").isEmpty());
+#endif
+
+ QFETCH(QString, name);
+ QFETCH(QString, value);
+ QString executable = QDir::currentPath() + "/testProcessEnvironment/testProcessEnvironment";
+
+ {
+ QProcess process;
+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+ if (value.isNull())
+ environment.remove(name);
+ else
+ environment.insert(name, value);
+ process.setProcessEnvironment(environment);
+ process.start(executable, QStringList() << name);
+
+ QVERIFY(process.waitForFinished());
+ if (value.isNull())
+ QCOMPARE(process.exitCode(), 1);
+ else if (!value.isEmpty())
+ QCOMPARE(process.exitCode(), 0);
+
+ QCOMPARE(process.readAll(), value.toLocal8Bit());
+ }
+}
+//-----------------------------------------------------------------------------
+void tst_QProcess::systemEnvironment()
+{
+#if defined (Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
+ // there is no concept of system variables on Windows CE as there is no console
+ QVERIFY(QProcess::systemEnvironment().isEmpty());
+ QVERIFY(QProcessEnvironment::systemEnvironment().isEmpty());
+#else
+ QVERIFY(!QProcess::systemEnvironment().isEmpty());
+ QVERIFY(!QProcessEnvironment::systemEnvironment().isEmpty());
+
+ QVERIFY(QProcessEnvironment::systemEnvironment().contains("PATH"));
+ QVERIFY(!QProcess::systemEnvironment().filter(QRegExp("^PATH=", Qt::CaseInsensitive)).isEmpty());
+#endif
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::spaceInName()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+ QProcess process;
+ process.start("test Space In Name/testSpaceInName", QStringList());
+ QVERIFY(process.waitForStarted());
+ process.write("", 1);
+ QVERIFY(process.waitForFinished());
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::lockupsInStartDetached()
+{
+#if !defined(Q_OS_SYMBIAN)
+ // Check that QProcess doesn't cause a lock up at this program's
+ // exit if a thread was started and we tried to run a program that
+ // doesn't exist. Before Qt 4.2, this used to lock up on Unix due
+ // to calling ::exit instead of ::_exit if execve failed.
+
+ QHostInfo::lookupHost(QString("something.invalid"), 0, 0);
+ QProcess::execute("yjhbrty");
+ QProcess::startDetached("yjhbrty");
+#endif
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::atEnd2()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess process;
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho/testProcessEcho.app");
+#else
+ process.start("testProcessEcho/testProcessEcho");
+#endif
+ process.write("Foo\nBar\nBaz\nBodukon\nHadukan\nTorwukan\nend\n");
+ process.putChar('\0');
+ QVERIFY(process.waitForFinished());
+ QList<QByteArray> lines;
+ while (!process.atEnd()) {
+ lines << process.readLine();
+ }
+ QCOMPARE(lines.size(), 7);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::waitForReadyReadForNonexistantProcess()
+{
+ // This comes from task 108968
+ // Start a program that doesn't exist, process events and then try to waitForReadyRead
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
+
+ QProcess process;
+ QSignalSpy errorSpy(&process, SIGNAL(error(QProcess::ProcessError)));
+ QSignalSpy finishedSpy1(&process, SIGNAL(finished(int)));
+ QSignalSpy finishedSpy2(&process, SIGNAL(finished(int, QProcess::ExitStatus)));
+ QVERIFY(!process.waitForReadyRead()); // used to crash
+ process.start("doesntexist");
+ QVERIFY(!process.waitForReadyRead());
+ QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(errorSpy.at(0).at(0).toInt(), 0);
+ QCOMPARE(finishedSpy1.count(), 0);
+ QCOMPARE(finishedSpy2.count(), 0);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setStandardInputFile()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ static const char data[] = "A bunch\1of\2data\3\4\5\6\7...";
+ QProcess process;
+ QFile file("data");
+
+ QVERIFY(file.open(QIODevice::WriteOnly));
+ file.write(data, sizeof data);
+ file.close();
+
+ process.setStandardInputFile("data");
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho/testProcessEcho.app");
+#else
+ process.start("testProcessEcho/testProcessEcho");
+#endif
+
+ QPROCESS_VERIFY(process, waitForFinished());
+ QByteArray all = process.readAll();
+ QCOMPARE(all.size(), int(sizeof data) - 1); // testProcessEcho drops the ending \0
+ QVERIFY(all == data);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setStandardOutputFile_data()
+{
+ QTest::addColumn<int>("channelToTest");
+ QTest::addColumn<int>("_channelMode");
+ QTest::addColumn<bool>("append");
+
+ QTest::newRow("stdout-truncate") << int(QProcess::StandardOutput)
+ << int(QProcess::SeparateChannels)
+ << false;
+ QTest::newRow("stdout-append") << int(QProcess::StandardOutput)
+ << int(QProcess::SeparateChannels)
+ << true;
+
+ QTest::newRow("stderr-truncate") << int(QProcess::StandardError)
+ << int(QProcess::SeparateChannels)
+ << false;
+ QTest::newRow("stderr-append") << int(QProcess::StandardError)
+ << int(QProcess::SeparateChannels)
+ << true;
+
+ QTest::newRow("merged-truncate") << int(QProcess::StandardOutput)
+ << int(QProcess::MergedChannels)
+ << false;
+ QTest::newRow("merged-append") << int(QProcess::StandardOutput)
+ << int(QProcess::MergedChannels)
+ << true;
+}
+
+void tst_QProcess::setStandardOutputFile()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ static const char data[] = "Original data. ";
+ static const char testdata[] = "Test data.";
+
+ QFETCH(int, channelToTest);
+ QFETCH(int, _channelMode);
+ QFETCH(bool, append);
+
+ QProcess::ProcessChannelMode channelMode = QProcess::ProcessChannelMode(_channelMode);
+ QIODevice::OpenMode mode = append ? QIODevice::Append : QIODevice::Truncate;
+
+ // create the destination file with data
+ QFile file("data");
+ QVERIFY(file.open(QIODevice::WriteOnly));
+ file.write(data, sizeof data - 1);
+ file.close();
+
+ // run the process
+ QProcess process;
+ process.setReadChannelMode(channelMode);
+ if (channelToTest == QProcess::StandardOutput)
+ process.setStandardOutputFile("data", mode);
+ else
+ process.setStandardErrorFile("data", mode);
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ process.start("testProcessEcho2/testProcessEcho2");
+#endif
+ process.write(testdata, sizeof testdata);
+ QPROCESS_VERIFY(process,waitForFinished());
+
+ // open the file again and verify the data
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QByteArray all = file.readAll();
+ file.close();
+
+ int expectedsize = sizeof testdata - 1;
+ if (mode == QIODevice::Append) {
+ QVERIFY(all.startsWith(data));
+ expectedsize += sizeof data - 1;
+ }
+ if (channelMode == QProcess::MergedChannels) {
+ expectedsize += sizeof testdata - 1;
+ } else {
+ QVERIFY(all.endsWith(testdata));
+ }
+
+ QCOMPARE(all.size(), expectedsize);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setStandardOutputProcess_data()
+{
+ QTest::addColumn<bool>("merged");
+ QTest::newRow("separate") << false;
+ QTest::newRow("merged") << true;
+}
+
+void tst_QProcess::setStandardOutputProcess()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QProcess source;
+ QProcess sink;
+
+ QFETCH(bool, merged);
+ source.setReadChannelMode(merged ? QProcess::MergedChannels : QProcess::SeparateChannels);
+ source.setStandardOutputProcess(&sink);
+
+#ifdef Q_OS_MAC
+ source.start("testProcessEcho2/testProcessEcho2.app");
+ sink.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ source.start("testProcessEcho2/testProcessEcho2");
+ sink.start("testProcessEcho2/testProcessEcho2");
+#endif
+
+ QByteArray data("Hello, World");
+ source.write(data);
+ source.closeWriteChannel();
+ QPROCESS_VERIFY(source, waitForFinished());
+ QPROCESS_VERIFY(sink, waitForFinished());
+ QByteArray all = sink.readAll();
+
+ if (!merged)
+ QCOMPARE(all, data);
+ else
+ QCOMPARE(all, QByteArray("HHeelllloo,, WWoorrlldd"));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::fileWriterProcess()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+
+ QString stdinStr;
+ for (int i = 0; i < 5000; ++i)
+ stdinStr += QString::fromLatin1("%1 -- testing testing 1 2 3\n").arg(i);
+
+ QTime stopWatch;
+ stopWatch.start();
+ do {
+ QFile::remove("fileWriterProcess.txt");
+ QProcess process;
+ process.start("fileWriterProcess/fileWriterProcess",
+ QIODevice::ReadWrite | QIODevice::Text);
+ process.write(stdinStr.toLatin1());
+ process.closeWriteChannel();
+ while (process.bytesToWrite()) {
+ QVERIFY(stopWatch.elapsed() < 3500);
+ QVERIFY(process.waitForBytesWritten(2000));
+ }
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(QFile("fileWriterProcess.txt").size(), qint64(stdinStr.size()));
+ } while (stopWatch.elapsed() < 3000);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::detachedWorkingDirectoryAndPid()
+{
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86)
+ // WINSCW builds in Symbian do not allow multiple processes to load Qt libraries,
+ // so this test must be skipped.
+ QSKIP("Multiple processes loading Qt are not allowed in Qt/Symbian emulator.", SkipAll);
+#endif
+ qint64 pid;
+
+#ifdef Q_OS_WINCE
+ QTest::qSleep(1000);
+#endif
+
+#if defined(Q_OS_SYMBIAN)
+ // Symbian has no working directory support, so use logs dir as a shared directory
+ QFile infoFile(QLatin1String("c:\\logs\\detachedinfo.txt"));
+#else
+ QFile infoFile(QDir::currentPath() + QLatin1String("/detachedinfo.txt"));
+#endif
+ infoFile.remove();
+
+ QString workingDir = QDir::currentPath() + "/testDetached";
+
+#ifndef Q_OS_SYMBIAN
+ QVERIFY(QFile::exists(workingDir));
+#endif
+
+ QStringList args;
+ args << infoFile.fileName();
+ QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid));
+
+ QFileInfo fi(infoFile);
+ fi.setCaching(false);
+ //The guard counter ensures the test does not hang if the sub process fails.
+ //Instead, the test will fail when trying to open & verify the sub process output file.
+ for (int guard = 0; guard < 100 && fi.size() == 0; guard++) {
+ QTest::qSleep(100);
+ }
+
+ QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString actualWorkingDir = QString::fromUtf8(infoFile.readLine());
+ actualWorkingDir.chop(1); // strip off newline
+ QByteArray processIdString = infoFile.readLine();
+ processIdString.chop(1);
+ infoFile.close();
+ infoFile.remove();
+
+ bool ok = false;
+ qint64 actualPid = processIdString.toLongLong(&ok);
+ QVERIFY(ok);
+
+#if defined(Q_OS_SYMBIAN)
+ QEXPECT_FAIL("", "Working directory is not supported on Qt/symbian", Continue);
+#endif
+ QCOMPARE(actualWorkingDir, workingDir);
+ QCOMPARE(actualPid, pid);
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::switchReadChannels()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Reading and writing to a process is not supported on Qt/CE", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Reading and writing to a process is not supported on Qt/Symbian", SkipAll);
+#endif
+ const char data[] = "ABCD";
+
+ QProcess process;
+
+#ifdef Q_OS_MAC
+ process.start("testProcessEcho2/testProcessEcho2.app");
+#else
+ process.start("testProcessEcho2/testProcessEcho2");
+#endif
+ process.write(data);
+ process.closeWriteChannel();
+ QVERIFY(process.waitForFinished(5000));
+
+ for (int i = 0; i < 4; ++i) {
+ process.setReadChannel(QProcess::StandardOutput);
+ QCOMPARE(process.read(1), QByteArray(&data[i], 1));
+ process.setReadChannel(QProcess::StandardError);
+ QCOMPARE(process.read(1), QByteArray(&data[i], 1));
+ }
+
+ process.ungetChar('D');
+ process.setReadChannel(QProcess::StandardOutput);
+ process.ungetChar('D');
+ process.setReadChannel(QProcess::StandardError);
+ QCOMPARE(process.read(1), QByteArray("D"));
+ process.setReadChannel(QProcess::StandardOutput);
+ QCOMPARE(process.read(1), QByteArray("D"));
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::setWorkingDirectory()
+{
+#ifdef Q_OS_WINCE
+ QSKIP("Windows CE does not support working directory logic", SkipAll);
+#endif
+#if defined(Q_OS_SYMBIAN)
+ QSKIP("Symbian does not support working directory logic", SkipAll);
+#endif
+ process = new QProcess;
+ process->setWorkingDirectory("test");
+#ifdef Q_OS_MAC
+ process->start("testSetWorkingDirectory/testSetWorkingDirectory.app");
+#else
+ process->start("testSetWorkingDirectory/testSetWorkingDirectory");
+#endif
+#ifndef Q_OS_WIN
+ QSKIP("setWorkingDirectory will chdir before starting the process on unices", SkipAll);
+#endif
+ QVERIFY(process->waitForFinished());
+
+ QByteArray workingDir = process->readAllStandardOutput();
+ QCOMPARE(QDir("test").canonicalPath(), QDir(workingDir.constData()).canonicalPath());
+
+ delete process;
+ process = 0;
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::startFinishStartFinish()
+{
+ QProcess process;
+
+ for (int i = 0; i < 3; ++i) {
+ QCOMPARE(process.state(), QProcess::NotRunning);
+
+#ifdef Q_OS_MAC
+ process.start("testProcessOutput/testProcessOutput.app");
+#else
+ process.start("testProcessOutput/testProcessOutput");
+#endif
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QVERIFY(process.waitForReadyRead(10000));
+ QCOMPARE(QString::fromLatin1(process.readLine().trimmed()),
+ QString("0 -this is a number"));
+#endif
+ if (process.state() != QProcess::NotRunning)
+ QVERIFY(process.waitForFinished(10000));
+#if defined(Q_OS_SYMBIAN)
+ // Symbian test outputs to a file, so check that
+ FILE* file = fopen("c:\\logs\\qprocess_output_test.txt","r");
+ QVERIFY(file);
+ char buf[30];
+ fgets(buf, 30, file);
+ QCOMPARE(QString::fromLatin1(buf),
+ QString("0 -this is a number\n"));
+ fclose(file);
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+void tst_QProcess::invalidProgramString_data()
+{
+ QTest::addColumn<QString>("programString");
+ QTest::newRow("null string") << QString();
+ QTest::newRow("empty string") << QString("");
+ QTest::newRow("only blank string") << QString(" ");
+}
+
+void tst_QProcess::invalidProgramString()
+{
+ QFETCH(QString, programString);
+ QProcess process;
+
+ qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");
+ QSignalSpy spy(&process, SIGNAL(error(QProcess::ProcessError)));
+
+ process.start(programString);
+ QCOMPARE(process.error(), QProcess::FailedToStart);
+ QCOMPARE(spy.count(), 1);
+
+ QVERIFY(!QProcess::startDetached(programString));
+}
+
+QTEST_MAIN(tst_QProcess)
+#include "tst_qprocess.moc"
+#endif
+