SSH: Derive SshRemoteProcess from QIODevice.

Now it looks even more like QProcess. Things like process channels are
still missing.

Change-Id: I3f30cd00ed4a054d02e83add9a6f4162b48f8345
Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
This commit is contained in:
Christian Kandeler
2011-11-15 17:13:02 +01:00
parent d9dde0d5e8
commit 4c76e40617
17 changed files with 171 additions and 91 deletions

View File

@@ -821,13 +821,13 @@ void SftpChannelPrivate::handleOpenSuccessInternal()
m_sftpState = SubsystemRequested; m_sftpState = SubsystemRequested;
} }
void SftpChannelPrivate::handleOpenFailureInternal() void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason)
{ {
if (channelState() != SessionRequested) { if (channelState() != SessionRequested) {
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
"Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet."); "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
} }
emit initializationFailed(tr("Server could not start session.")); emit initializationFailed(tr("Server could not start session: %1").arg(reason));
} }
void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job, void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job,

View File

@@ -74,7 +74,7 @@ private:
SftpJobId createJob(const AbstractSftpOperation::Ptr &job); SftpJobId createJob(const AbstractSftpOperation::Ptr &job);
virtual void handleOpenSuccessInternal(); virtual void handleOpenSuccessInternal();
virtual void handleOpenFailureInternal(); virtual void handleOpenFailureInternal(const QString &reason);
virtual void handleChannelDataInternal(const QByteArray &data); virtual void handleChannelDataInternal(const QByteArray &data);
virtual void handleChannelExtendedDataInternal(quint32 type, virtual void handleChannelExtendedDataInternal(quint32 type,
const QByteArray &data); const QByteArray &data);

View File

@@ -84,7 +84,7 @@ void AbstractSshChannel::requestSessionStart()
setChannelState(SessionRequested); setChannelState(SessionRequested);
m_timeoutTimer->start(ReplyTimeout); m_timeoutTimer->start(ReplyTimeout);
} catch (Botan::Exception &e) { } catch (Botan::Exception &e) {
m_errorString = QString::fromAscii(e.what()); qDebug("Botan error: %s", e.what());
closeChannel(); closeChannel();
} }
} }
@@ -95,7 +95,7 @@ void AbstractSshChannel::sendData(const QByteArray &data)
m_sendBuffer += data; m_sendBuffer += data;
flushSendBuffer(); flushSendBuffer();
} catch (Botan::Exception &e) { } catch (Botan::Exception &e) {
m_errorString = QString::fromAscii(e.what()); qDebug("Botan error: %s", e.what());
closeChannel(); closeChannel();
} }
} }
@@ -163,8 +163,7 @@ void AbstractSshChannel::handleOpenFailure(const QString &reason)
#ifdef CREATOR_SSH_DEBUG #ifdef CREATOR_SSH_DEBUG
qDebug("Channel open request failed for channel %u", m_localChannel); qDebug("Channel open request failed for channel %u", m_localChannel);
#endif #endif
m_errorString = reason; handleOpenFailureInternal(reason);
handleOpenFailureInternal();
} }
void AbstractSshChannel::handleChannelEof() void AbstractSshChannel::handleChannelEof()

View File

@@ -58,9 +58,6 @@ public:
ChannelState channelState() const { return m_state; } ChannelState channelState() const { return m_state; }
void setChannelState(ChannelState state); void setChannelState(ChannelState state);
void setError(const QString &error) { m_errorString = error; }
QString errorString() const { return m_errorString; }
quint32 localChannelId() const { return m_localChannel; } quint32 localChannelId() const { return m_localChannel; }
quint32 remoteChannel() const { return m_remoteChannel; } quint32 remoteChannel() const { return m_remoteChannel; }
@@ -101,7 +98,7 @@ protected:
private: private:
virtual void handleOpenSuccessInternal() = 0; virtual void handleOpenSuccessInternal() = 0;
virtual void handleOpenFailureInternal() = 0; virtual void handleOpenFailureInternal(const QString &reason) = 0;
virtual void handleChannelDataInternal(const QByteArray &data) = 0; virtual void handleChannelDataInternal(const QByteArray &data) = 0;
virtual void handleChannelExtendedDataInternal(quint32 type, virtual void handleChannelExtendedDataInternal(quint32 type,
const QByteArray &data) = 0; const QByteArray &data) = 0;
@@ -119,7 +116,6 @@ private:
quint32 m_remoteMaxPacketSize; quint32 m_remoteMaxPacketSize;
ChannelState m_state; ChannelState m_state;
QByteArray m_sendBuffer; QByteArray m_sendBuffer;
QString m_errorString;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -42,6 +42,8 @@
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <cstring>
/*! /*!
\class Utils::SshRemoteProcess \class Utils::SshRemoteProcess
@@ -49,17 +51,10 @@
Objects are created via SshConnection::createRemoteProcess. Objects are created via SshConnection::createRemoteProcess.
The process is started via the start() member function. The process is started via the start() member function.
A closeChannel() function is provided, but rarely useful, because
\list
\i a) when the process ends, the channel is closed automatically, and
\i b) closing a channel will not necessarily kill the remote process.
\endlist
Therefore, the only sensible use case for calling closeChannel() is to
get rid of an SshRemoteProces object before the process is actually started.
If the process needs a pseudo terminal, you can request one If the process needs a pseudo terminal, you can request one
via requestTerminal() before calling start(). via requestTerminal() before calling start().
Note that this class does not support QIODevice's waitFor*() functions, i.e. it has
no synchronous mode.
*/ */
namespace Utils { namespace Utils {
@@ -99,12 +94,63 @@ SshRemoteProcess::~SshRemoteProcess()
delete d; delete d;
} }
bool SshRemoteProcess::atEnd() const
{
return QIODevice::atEnd() && d->m_stdout.isEmpty();
}
qint64 SshRemoteProcess::bytesAvailable() const
{
return QIODevice::bytesAvailable() + d->m_stdout.count();
}
bool SshRemoteProcess::canReadLine() const
{
return QIODevice::canReadLine() || d->m_stdout.contains('\n'); // TODO: Not cross-platform?
}
QByteArray SshRemoteProcess::readAllStandardOutput()
{
return readAll();
}
QByteArray SshRemoteProcess::readAllStandardError()
{
const QByteArray data = d->m_stderr;
d->m_stderr.clear();
return data;
}
void SshRemoteProcess::close()
{
d->closeChannel();
QIODevice::close();
}
qint64 SshRemoteProcess::readData(char *data, qint64 maxlen)
{
const qint64 bytesRead = qMin(qint64(d->m_stdout.count()), maxlen);
memcpy(data, d->m_stdout.constData(), bytesRead);
d->m_stdout.remove(0, bytesRead);
return bytesRead;
}
qint64 SshRemoteProcess::writeData(const char *data, qint64 len)
{
if (isRunning()) {
d->sendData(QByteArray(data, len));
return len;
}
return 0;
}
void SshRemoteProcess::init() void SshRemoteProcess::init()
{ {
connect(d, SIGNAL(started()), this, SIGNAL(started()), connect(d, SIGNAL(started()), this, SIGNAL(started()),
Qt::QueuedConnection); Qt::QueuedConnection);
connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()), connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()),
Qt::QueuedConnection); Qt::QueuedConnection);
connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyRead()), Qt::QueuedConnection);
connect(d, SIGNAL(readyReadStandardError()), this, connect(d, SIGNAL(readyReadStandardError()), this,
SIGNAL(readyReadStandardError()), Qt::QueuedConnection); SIGNAL(readyReadStandardError()), Qt::QueuedConnection);
connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection); connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection);
@@ -129,6 +175,7 @@ void SshRemoteProcess::start()
#ifdef CREATOR_SSH_DEBUG #ifdef CREATOR_SSH_DEBUG
qDebug("process start requested, channel id = %u", d->localChannelId()); qDebug("process start requested, channel id = %u", d->localChannelId());
#endif #endif
QIODevice::open(QIODevice::ReadWrite);
d->requestSessionStart(); d->requestSessionStart();
} }
} }
@@ -140,36 +187,19 @@ void SshRemoteProcess::sendSignal(const QByteArray &signal)
d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(), d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(),
signal); signal);
} catch (Botan::Exception &e) { } catch (Botan::Exception &e) {
d->setError(QString::fromAscii(e.what())); setErrorString(QString::fromAscii(e.what()));
d->closeChannel(); d->closeChannel();
} }
} }
void SshRemoteProcess::closeChannel()
{
d->closeChannel();
}
void SshRemoteProcess::sendInput(const QByteArray &data)
{
if (isRunning())
d->sendData(data);
}
bool SshRemoteProcess::isRunning() const bool SshRemoteProcess::isRunning() const
{ {
return d->m_procState == Internal::SshRemoteProcessPrivate::Running; return d->m_procState == Internal::SshRemoteProcessPrivate::Running;
} }
QString SshRemoteProcess::errorString() const { return d->errorString(); }
int SshRemoteProcess::exitCode() const { return d->m_exitCode; } int SshRemoteProcess::exitCode() const { return d->m_exitCode; }
QByteArray SshRemoteProcess::exitSignal() const { return d->m_signal; } QByteArray SshRemoteProcess::exitSignal() const { return d->m_signal; }
QByteArray SshRemoteProcess::readAllStandardOutput() { return d->readAllStandardOutput(); }
QByteArray SshRemoteProcess::readAllStandardError() { return d->readAllStandardError(); }
namespace Internal { namespace Internal {
SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command, SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command,
@@ -214,20 +244,6 @@ void SshRemoteProcessPrivate::setProcState(ProcessState newState)
} }
} }
QByteArray SshRemoteProcessPrivate::readAllStandardOutput()
{
const QByteArray data = m_stdout;
m_stdout.clear();
return data;
}
QByteArray SshRemoteProcessPrivate::readAllStandardError()
{
const QByteArray data = m_stderr;
m_stderr.clear();
return data;
}
void SshRemoteProcessPrivate::closeHook() void SshRemoteProcessPrivate::closeHook()
{ {
if (m_wasRunning) { if (m_wasRunning) {
@@ -256,9 +272,10 @@ void SshRemoteProcessPrivate::handleOpenSuccessInternal()
m_timeoutTimer->start(ReplyTimeout); m_timeoutTimer->start(ReplyTimeout);
} }
void SshRemoteProcessPrivate::handleOpenFailureInternal() void SshRemoteProcessPrivate::handleOpenFailureInternal(const QString &reason)
{ {
setProcState(StartFailed); setProcState(StartFailed);
m_proc->setErrorString(reason);
} }
void SshRemoteProcessPrivate::handleChannelSuccess() void SshRemoteProcessPrivate::handleChannelSuccess()
@@ -313,9 +330,9 @@ void SshRemoteProcessPrivate::handleExitSignal(const SshChannelExitSignal &signa
#ifdef CREATOR_SSH_DEBUG #ifdef CREATOR_SSH_DEBUG
qDebug("Exit due to signal %s", signal.signal.data()); qDebug("Exit due to signal %s", signal.signal.data());
#endif #endif
setError(signal.error);
m_signal = signal.signal; m_signal = signal.signal;
m_procState = Exited; m_procState = Exited;
m_proc->setErrorString(tr("Process killed by signal"));
} }
} // namespace Internal } // namespace Internal

View File

@@ -35,7 +35,7 @@
#include <utils/utils_global.h> #include <utils/utils_global.h>
#include <QtCore/QObject> #include <QtCore/QProcess>
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@@ -50,7 +50,8 @@ class SshRemoteProcessPrivate;
class SshSendFacility; class SshSendFacility;
} // namespace Internal } // namespace Internal
class QTCREATOR_UTILS_EXPORT SshRemoteProcess : public QObject // TODO: ProcessChannel
class QTCREATOR_UTILS_EXPORT SshRemoteProcess : public QIODevice
{ {
Q_OBJECT Q_OBJECT
@@ -77,6 +78,13 @@ public:
~SshRemoteProcess(); ~SshRemoteProcess();
// QIODevice stuff
bool atEnd() const;
qint64 bytesAvailable() const;
bool canReadLine() const;
void close();
bool isSequential() const { return true; }
/* /*
* Note that this is of limited value in practice, because servers are * Note that this is of limited value in practice, because servers are
* usually configured to ignore such requests for security reasons. * usually configured to ignore such requests for security reasons.
@@ -85,10 +93,8 @@ public:
void requestTerminal(const SshPseudoTerminal &terminal); void requestTerminal(const SshPseudoTerminal &terminal);
void start(); void start();
void closeChannel();
bool isRunning() const; bool isRunning() const;
QString errorString() const;
int exitCode() const; int exitCode() const;
QByteArray exitSignal() const; QByteArray exitSignal() const;
@@ -99,8 +105,6 @@ public:
void sendSignal(const QByteArray &signal); void sendSignal(const QByteArray &signal);
void kill() { sendSignal(KillSignal); } void kill() { sendSignal(KillSignal); }
void sendInput(const QByteArray &data); // Should usually have a trailing newline.
signals: signals:
void started(); void started();
@@ -118,6 +122,10 @@ private:
Internal::SshSendFacility &sendFacility); Internal::SshSendFacility &sendFacility);
SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility); SshRemoteProcess(quint32 channelId, Internal::SshSendFacility &sendFacility);
// QIODevice stuff
qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len);
void init(); void init();
Internal::SshRemoteProcessPrivate *d; Internal::SshRemoteProcessPrivate *d;

View File

@@ -60,9 +60,6 @@ public:
virtual void closeHook(); virtual void closeHook();
QByteArray readAllStandardOutput();
QByteArray readAllStandardError();
signals: signals:
void started(); void started();
void readyReadStandardOutput(); void readyReadStandardOutput();
@@ -76,7 +73,7 @@ private:
SshRemoteProcess *proc); SshRemoteProcess *proc);
virtual void handleOpenSuccessInternal(); virtual void handleOpenSuccessInternal();
virtual void handleOpenFailureInternal(); virtual void handleOpenFailureInternal(const QString &reason);
virtual void handleChannelDataInternal(const QByteArray &data); virtual void handleChannelDataInternal(const QByteArray &data);
virtual void handleChannelExtendedDataInternal(quint32 type, virtual void handleChannelExtendedDataInternal(quint32 type,
const QByteArray &data); const QByteArray &data);

View File

@@ -341,7 +341,7 @@ void RemoteGdbProcess::sendInput(const QByteArray &data)
if (!isdigit(data.at(pos))) if (!isdigit(data.at(pos)))
break; break;
m_lastSeqNr = data.left(pos); m_lastSeqNr = data.left(pos);
m_gdbProc->sendInput(data); m_gdbProc->write(data);
} }
void RemoteGdbProcess::handleAppOutput() void RemoteGdbProcess::handleAppOutput()

View File

@@ -95,7 +95,7 @@ qint64 SshIODevice::writeData (const char * data, qint64 maxSize)
startupbuffer += QByteArray::fromRawData(data, maxSize); startupbuffer += QByteArray::fromRawData(data, maxSize);
return maxSize; return maxSize;
} }
proc->sendInput(QByteArray::fromRawData(data, maxSize)); proc->write(data, maxSize);
return maxSize; return maxSize;
} }
qint64 SshIODevice::readData (char * data, qint64 maxSize) qint64 SshIODevice::readData (char * data, qint64 maxSize)
@@ -128,7 +128,7 @@ qint64 SshIODevice::readData (char * data, qint64 maxSize)
void SshIODevice::processStarted() void SshIODevice::processStarted()
{ {
proc = runner->process(); proc = runner->process();
proc->sendInput(startupbuffer); proc->write(startupbuffer);
} }
void SshIODevice::outputAvailable(const QByteArray &output) void SshIODevice::outputAvailable(const QByteArray &output)

View File

@@ -89,7 +89,7 @@ void MaddeDeviceTester::stopTest()
case QtTest: case QtTest:
case MadDeveloperTest: case MadDeveloperTest:
case QmlToolingTest: case QmlToolingTest:
m_processRunner->process()->closeChannel(); m_processRunner->process()->close();
break; break;
} }

View File

@@ -447,7 +447,7 @@ void MaemoPublisherFremantleFree::prepareToSendFile()
emit progressReport(tr("Uploading file %1 ...") emit progressReport(tr("Uploading file %1 ...")
.arg(QDir::toNativeSeparators(nextFilePath))); .arg(QDir::toNativeSeparators(nextFilePath)));
QFileInfo info(nextFilePath); QFileInfo info(nextFilePath);
m_uploader->process()->sendInput("C0644 " + QByteArray::number(info.size()) m_uploader->process()->write("C0644 " + QByteArray::number(info.size())
+ ' ' + info.fileName().toUtf8() + '\n'); + ' ' + info.fileName().toUtf8() + '\n');
} }
@@ -473,13 +473,13 @@ void MaemoPublisherFremantleFree::sendFile()
tr("Upload failed.")); tr("Upload failed."));
return; return;
} }
m_uploader->process()->sendInput(data); m_uploader->process()->write(data);
bytesToSend -= data.size(); bytesToSend -= data.size();
QCoreApplication::processEvents(); QCoreApplication::processEvents();
if (m_state == Inactive) if (m_state == Inactive)
return; return;
} }
m_uploader->process()->sendInput(QByteArray(1, '\0')); m_uploader->process()->write(QByteArray(1, '\0'));
} }
void MaemoPublisherFremantleFree::handleScpStdOut(const QByteArray &output) void MaemoPublisherFremantleFree::handleScpStdOut(const QByteArray &output)

View File

@@ -380,11 +380,11 @@ void MaemoRemoteMounter::setState(State newState)
m_utfsServerTimer->stop(); m_utfsServerTimer->stop();
if (m_mountProcess) { if (m_mountProcess) {
disconnect(m_mountProcess.data(), 0, this, 0); disconnect(m_mountProcess.data(), 0, this, 0);
m_mountProcess->closeChannel(); m_mountProcess->close();
} }
if (m_unmountProcess) { if (m_unmountProcess) {
disconnect(m_unmountProcess.data(), 0, this, 0); disconnect(m_unmountProcess.data(), 0, this, 0);
m_unmountProcess->closeChannel(); m_unmountProcess->close();
} }
} }
m_state = newState; m_state = newState;

View File

@@ -106,7 +106,7 @@ void GenericLinuxDeviceTester::stopTest()
d->portsGatherer.stop(); d->portsGatherer.stop();
break; break;
case RunningUname: case RunningUname:
d->process->closeChannel(); d->process->close();
break; break;
case Inactive: case Inactive:
break; break;

View File

@@ -115,7 +115,7 @@ void RemoteLinuxCustomCommandDeployService::stopDeployment()
QTC_ASSERT(d->state == Running, return); QTC_ASSERT(d->state == Running, return);
disconnect(d->runner, 0, this, 0); disconnect(d->runner, 0, this, 0);
d->runner->process()->closeChannel(); d->runner->process()->close();
d->state = Inactive; d->state = Inactive;
handleDeploymentDone(); handleDeploymentDone();
} }

View File

@@ -35,6 +35,7 @@
#include <utils/ssh/sshpseudoterminal.h> #include <utils/ssh/sshpseudoterminal.h>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QTextStream>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <iostream> #include <iostream>
@@ -75,9 +76,10 @@ void RemoteProcessTest::run()
void RemoteProcessTest::handleConnectionError() void RemoteProcessTest::handleConnectionError()
{ {
std::cerr << "Error: Connection failure (" const QString error = m_state == TestingIoDevice
<< qPrintable(m_remoteRunner->lastConnectionErrorString()) << ")." ? m_sshConnection->errorString() : m_remoteRunner->lastConnectionErrorString();
<< std::endl;
std::cerr << "Error: Connection failure (" << qPrintable(error) << ")." << std::endl;
qApp->quit(); qApp->quit();
} }
@@ -92,6 +94,11 @@ void RemoteProcessTest::handleProcessStarted()
Utils::SshRemoteProcessRunner * const killer Utils::SshRemoteProcessRunner * const killer
= new Utils::SshRemoteProcessRunner(this); = new Utils::SshRemoteProcessRunner(this);
killer->run("pkill -9 sleep", m_sshParams); killer->run("pkill -9 sleep", m_sshParams);
} else if (m_state == TestingIoDevice) {
connect(m_catProcess.data(), SIGNAL(readyRead()), SLOT(handleReadyRead()));
m_textStream = new QTextStream(m_catProcess.data());
*m_textStream << testString();
m_textStream->flush();
} }
} }
} }
@@ -198,10 +205,16 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
qApp->quit(); qApp->quit();
return; return;
} }
std::cout << "Ok.\nAll tests succeeded." << std::endl; std::cout << "Ok.\nTesting I/O device functionality... " << std::flush;
qApp->quit(); m_state = TestingIoDevice;
m_sshConnection = Utils::SshConnection::create(m_sshParams);
connect(m_sshConnection.data(), SIGNAL(connected()), SLOT(handleConnected()));
connect(m_sshConnection.data(), SIGNAL(error(Utils::SshError)),
SLOT(handleConnectionError()));
m_sshConnection->connectToHost();
break; break;
} }
case TestingIoDevice:
case Inactive: case Inactive:
Q_ASSERT(false); Q_ASSERT(false);
} }
@@ -216,16 +229,23 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
qApp->quit(); qApp->quit();
break; break;
case SshRemoteProcess::KilledBySignal: case SshRemoteProcess::KilledBySignal:
if (m_state != TestingCrash) { switch (m_state) {
std::cerr << "Error: Unexpected crash." << std::endl; case TestingCrash:
qApp->quit();
return;
}
std::cout << "Ok.\nTesting remote process with terminal... " << std::flush; std::cout << "Ok.\nTesting remote process with terminal... " << std::flush;
m_state = TestingTerminal; m_state = TestingTerminal;
m_started = false; m_started = false;
m_timeoutTimer->start(); m_timeoutTimer->start();
m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams); m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams);
break;
case TestingIoDevice:
std::cout << "Ok.\nAll tests succeeded." << std::endl;
qApp->quit();
break;
default:
std::cerr << "Error: Unexpected crash." << std::endl;
qApp->quit();
return;
}
} }
} }
@@ -234,3 +254,35 @@ void RemoteProcessTest::handleTimeout()
std::cerr << "Error: Timeout waiting for progress." << std::endl; std::cerr << "Error: Timeout waiting for progress." << std::endl;
qApp->quit(); qApp->quit();
} }
void RemoteProcessTest::handleConnected()
{
Q_ASSERT(m_state == TestingIoDevice);
m_catProcess = m_sshConnection->createRemoteProcess(QString::fromLocal8Bit("cat").toUtf8());
connect(m_catProcess.data(), SIGNAL(started()), SLOT(handleProcessStarted()));
connect(m_catProcess.data(), SIGNAL(closed(int)), SLOT(handleProcessClosed(int)));
m_started = false;
m_timeoutTimer->start();
m_catProcess->start();
}
QString RemoteProcessTest::testString() const
{
return QLatin1String("x");
}
void RemoteProcessTest::handleReadyRead()
{
Q_ASSERT(m_state == TestingIoDevice);
const QString &data = QString::fromUtf8(m_catProcess->readAll());
if (data != testString()) {
std::cerr << "Testing of QIODevice functionality failed: Expected '"
<< qPrintable(testString()) << "', got '" << qPrintable(data) << "'." << std::endl;
qApp->exit(1);
}
Utils::SshRemoteProcessRunner * const killer = new Utils::SshRemoteProcessRunner(this);
killer->run("pkill -9 cat", m_sshParams);
}

View File

@@ -35,9 +35,11 @@
#include <utils/ssh/sshremoteprocessrunner.h> #include <utils/ssh/sshremoteprocessrunner.h>
QT_FORWARD_DECLARE_CLASS(QTimer);
#include <QtCore/QObject> #include <QtCore/QObject>
QT_FORWARD_DECLARE_CLASS(QTextStream)
QT_FORWARD_DECLARE_CLASS(QTimer)
class RemoteProcessTest : public QObject class RemoteProcessTest : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -53,13 +55,22 @@ private slots:
void handleProcessStderr(const QByteArray &output); void handleProcessStderr(const QByteArray &output);
void handleProcessClosed(int exitStatus); void handleProcessClosed(int exitStatus);
void handleTimeout(); void handleTimeout();
void handleReadyRead();
void handleConnected();
private: private:
enum State { Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal }; enum State {
Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice
};
QString testString() const;
const Utils::SshConnectionParameters m_sshParams; const Utils::SshConnectionParameters m_sshParams;
QTimer * const m_timeoutTimer; QTimer * const m_timeoutTimer;
QTextStream *m_textStream;
Utils::SshRemoteProcessRunner * const m_remoteRunner; Utils::SshRemoteProcessRunner * const m_remoteRunner;
Utils::SshRemoteProcess::Ptr m_catProcess;
Utils::SshConnection::Ptr m_sshConnection;
QByteArray m_remoteStdout; QByteArray m_remoteStdout;
QByteArray m_remoteStderr; QByteArray m_remoteStderr;
State m_state; State m_state;

View File

@@ -115,5 +115,5 @@ void Shell::handleChannelClosed(int exitStatus)
void Shell::handleStdin() void Shell::handleStdin()
{ {
m_shell->sendInput(m_stdin->readLine()); m_shell->write(m_stdin->readLine());
} }