/****************************************************************************+ −
**+ −
** Copyright (C) 2010 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 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();+ −
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));+ −
}+ −
+ −
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);+ −
QVERIFY(process->waitForStarted(5000));+ −
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(process->state(), 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::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");+ −
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");+ −
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;+ −
}+ −
+ −
//-----------------------------------------------------------------------------+ −
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)+ −
// What exactly is this call supposed to achieve anyway?+ −
QHostInfo::lookupHost(QString("something.invalid"), 0, 0);+ −
#endif+ −
QProcess::execute("yjhbrty");+ −
QProcess::startDetached("yjhbrty");+ −
}+ −
+ −
//-----------------------------------------------------------------------------+ −
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);+ −
while (fi.size() == 0) {+ −
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");+ −
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+ −
+ −