qt5base-lts/tests/auto/qprocess/tst_qprocess.cpp
Qt by Nokia 38be0d1383 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
2011-04-27 12:05:43 +02:00

2449 lines
77 KiB
C++

/****************************************************************************
**
** 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