diff --git a/src/libs/utils/ssh/sftpchannel.cpp b/src/libs/utils/ssh/sftpchannel.cpp
index c17ce21d19362574e70c007b96929cf6df41c23f..79e1f3f2ef6c2eb60385384e886daf2675ab503d 100644
--- a/src/libs/utils/ssh/sftpchannel.cpp
+++ b/src/libs/utils/ssh/sftpchannel.cpp
@@ -821,13 +821,13 @@ void SftpChannelPrivate::handleOpenSuccessInternal()
     m_sftpState = SubsystemRequested;
 }
 
-void SftpChannelPrivate::handleOpenFailureInternal()
+void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason)
 {
     if (channelState() != SessionRequested) {
         throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
             "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,
diff --git a/src/libs/utils/ssh/sftpchannel_p.h b/src/libs/utils/ssh/sftpchannel_p.h
index 241cbb77a7ba52193a7db175a28f2b381e46bfae..e2efd89afd77dbc672347f11995893cb827543ac 100644
--- a/src/libs/utils/ssh/sftpchannel_p.h
+++ b/src/libs/utils/ssh/sftpchannel_p.h
@@ -74,7 +74,7 @@ private:
     SftpJobId createJob(const AbstractSftpOperation::Ptr &job);
 
     virtual void handleOpenSuccessInternal();
-    virtual void handleOpenFailureInternal();
+    virtual void handleOpenFailureInternal(const QString &reason);
     virtual void handleChannelDataInternal(const QByteArray &data);
     virtual void handleChannelExtendedDataInternal(quint32 type,
         const QByteArray &data);
diff --git a/src/libs/utils/ssh/sshchannel.cpp b/src/libs/utils/ssh/sshchannel.cpp
index b6965da5b1a2a7a47220f6fc05f374ec1077c9cf..4fa68dc89e318911f8625249c7eb4eef0827d2d1 100644
--- a/src/libs/utils/ssh/sshchannel.cpp
+++ b/src/libs/utils/ssh/sshchannel.cpp
@@ -84,7 +84,7 @@ void AbstractSshChannel::requestSessionStart()
         setChannelState(SessionRequested);
         m_timeoutTimer->start(ReplyTimeout);
     }  catch (Botan::Exception &e) {
-        m_errorString = QString::fromAscii(e.what());
+        qDebug("Botan error: %s", e.what());
         closeChannel();
     }
 }
@@ -95,7 +95,7 @@ void AbstractSshChannel::sendData(const QByteArray &data)
         m_sendBuffer += data;
         flushSendBuffer();
     }  catch (Botan::Exception &e) {
-        m_errorString = QString::fromAscii(e.what());
+        qDebug("Botan error: %s", e.what());
         closeChannel();
     }
 }
@@ -163,8 +163,7 @@ void AbstractSshChannel::handleOpenFailure(const QString &reason)
 #ifdef CREATOR_SSH_DEBUG
    qDebug("Channel open request failed for channel %u", m_localChannel);
 #endif
-   m_errorString = reason;
-   handleOpenFailureInternal();
+   handleOpenFailureInternal(reason);
 }
 
 void AbstractSshChannel::handleChannelEof()
diff --git a/src/libs/utils/ssh/sshchannel_p.h b/src/libs/utils/ssh/sshchannel_p.h
index 201b77b9703e9914ca9727204ccb16cf07dd5606..c6c81f5abf3585319650f21645bb36bdbaf55be8 100644
--- a/src/libs/utils/ssh/sshchannel_p.h
+++ b/src/libs/utils/ssh/sshchannel_p.h
@@ -58,9 +58,6 @@ public:
     ChannelState channelState() const { return m_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 remoteChannel() const { return m_remoteChannel; }
 
@@ -101,7 +98,7 @@ protected:
 
 private:
     virtual void handleOpenSuccessInternal() = 0;
-    virtual void handleOpenFailureInternal() = 0;
+    virtual void handleOpenFailureInternal(const QString &reason) = 0;
     virtual void handleChannelDataInternal(const QByteArray &data) = 0;
     virtual void handleChannelExtendedDataInternal(quint32 type,
         const QByteArray &data) = 0;
@@ -119,7 +116,6 @@ private:
     quint32 m_remoteMaxPacketSize;
     ChannelState m_state;
     QByteArray m_sendBuffer;
-    QString m_errorString;
 };
 
 } // namespace Internal
diff --git a/src/libs/utils/ssh/sshremoteprocess.cpp b/src/libs/utils/ssh/sshremoteprocess.cpp
index 90ab74735fff3056a1a18845f4450e580e680fd3..52068ba86948efb237fd1cc02b6219371b73a158 100644
--- a/src/libs/utils/ssh/sshremoteprocess.cpp
+++ b/src/libs/utils/ssh/sshremoteprocess.cpp
@@ -42,6 +42,8 @@
 
 #include <QtCore/QTimer>
 
+#include <cstring>
+
 /*!
     \class Utils::SshRemoteProcess
 
@@ -49,17 +51,10 @@
 
     Objects are created via SshConnection::createRemoteProcess.
     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
     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 {
@@ -99,12 +94,63 @@ SshRemoteProcess::~SshRemoteProcess()
     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()
 {
     connect(d, SIGNAL(started()), this, SIGNAL(started()),
         Qt::QueuedConnection);
     connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyReadStandardOutput()),
         Qt::QueuedConnection);
+    connect(d, SIGNAL(readyReadStandardOutput()), this, SIGNAL(readyRead()), Qt::QueuedConnection);
     connect(d, SIGNAL(readyReadStandardError()), this,
         SIGNAL(readyReadStandardError()), Qt::QueuedConnection);
     connect(d, SIGNAL(closed(int)), this, SIGNAL(closed(int)), Qt::QueuedConnection);
@@ -129,6 +175,7 @@ void SshRemoteProcess::start()
 #ifdef CREATOR_SSH_DEBUG
         qDebug("process start requested, channel id = %u", d->localChannelId());
 #endif
+        QIODevice::open(QIODevice::ReadWrite);
         d->requestSessionStart();
     }
 }
@@ -140,36 +187,19 @@ void SshRemoteProcess::sendSignal(const QByteArray &signal)
             d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(),
                 signal);
     }  catch (Botan::Exception &e) {
-        d->setError(QString::fromAscii(e.what()));
+        setErrorString(QString::fromAscii(e.what()));
         d->closeChannel();
     }
 }
 
-void SshRemoteProcess::closeChannel()
-{
-    d->closeChannel();
-}
-
-void SshRemoteProcess::sendInput(const QByteArray &data)
-{
-    if (isRunning())
-        d->sendData(data);
-}
-
 bool SshRemoteProcess::isRunning() const
 {
     return d->m_procState == Internal::SshRemoteProcessPrivate::Running;
 }
 
-QString SshRemoteProcess::errorString() const { return d->errorString(); }
-
 int SshRemoteProcess::exitCode() const { return d->m_exitCode; }
-
 QByteArray SshRemoteProcess::exitSignal() const { return d->m_signal; }
 
-QByteArray SshRemoteProcess::readAllStandardOutput() { return d->readAllStandardOutput(); }
-QByteArray SshRemoteProcess::readAllStandardError() { return d->readAllStandardError(); }
-
 namespace Internal {
 
 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()
 {
     if (m_wasRunning) {
@@ -256,9 +272,10 @@ void SshRemoteProcessPrivate::handleOpenSuccessInternal()
    m_timeoutTimer->start(ReplyTimeout);
 }
 
-void SshRemoteProcessPrivate::handleOpenFailureInternal()
+void SshRemoteProcessPrivate::handleOpenFailureInternal(const QString &reason)
 {
    setProcState(StartFailed);
+   m_proc->setErrorString(reason);
 }
 
 void SshRemoteProcessPrivate::handleChannelSuccess()
@@ -313,9 +330,9 @@ void SshRemoteProcessPrivate::handleExitSignal(const SshChannelExitSignal &signa
 #ifdef CREATOR_SSH_DEBUG
     qDebug("Exit due to signal %s", signal.signal.data());
 #endif
-    setError(signal.error);
     m_signal = signal.signal;
     m_procState = Exited;
+    m_proc->setErrorString(tr("Process killed by signal"));
 }
 
 } // namespace Internal
diff --git a/src/libs/utils/ssh/sshremoteprocess.h b/src/libs/utils/ssh/sshremoteprocess.h
index 59f61385c20a946ed4acbc655286b1e4162f4a5e..fcd3f0edd546119a9bde15e26d2ab4943f8e9fb1 100644
--- a/src/libs/utils/ssh/sshremoteprocess.h
+++ b/src/libs/utils/ssh/sshremoteprocess.h
@@ -35,7 +35,7 @@
 
 #include <utils/utils_global.h>
 
-#include <QtCore/QObject>
+#include <QtCore/QProcess>
 #include <QtCore/QSharedPointer>
 
 QT_BEGIN_NAMESPACE
@@ -50,7 +50,8 @@ class SshRemoteProcessPrivate;
 class SshSendFacility;
 } // namespace Internal
 
-class QTCREATOR_UTILS_EXPORT SshRemoteProcess : public QObject
+// TODO: ProcessChannel
+class QTCREATOR_UTILS_EXPORT SshRemoteProcess : public QIODevice
 {
     Q_OBJECT
 
@@ -77,6 +78,13 @@ public:
 
     ~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
      * usually configured to ignore such requests for security reasons.
@@ -85,10 +93,8 @@ public:
 
     void requestTerminal(const SshPseudoTerminal &terminal);
     void start();
-    void closeChannel();
 
     bool isRunning() const;
-    QString errorString() const;
     int exitCode() const;
     QByteArray exitSignal() const;
 
@@ -99,8 +105,6 @@ public:
     void sendSignal(const QByteArray &signal);
     void kill() { sendSignal(KillSignal); }
 
-    void sendInput(const QByteArray &data); // Should usually have a trailing newline.
-
 signals:
     void started();
 
@@ -118,6 +122,10 @@ private:
         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();
 
     Internal::SshRemoteProcessPrivate *d;
diff --git a/src/libs/utils/ssh/sshremoteprocess_p.h b/src/libs/utils/ssh/sshremoteprocess_p.h
index da47927f3a0fd612948ecd38577b238dbdf5c609..32564ef9cbf86ff7f6e3ccc05003110e7e866dc3 100644
--- a/src/libs/utils/ssh/sshremoteprocess_p.h
+++ b/src/libs/utils/ssh/sshremoteprocess_p.h
@@ -52,7 +52,7 @@ class SshRemoteProcessPrivate : public AbstractSshChannel
     friend class Utils::SshRemoteProcess;
 public:
     enum ProcessState {
-        NotYetStarted, ExecRequested, StartFailed,Running, Exited
+        NotYetStarted, ExecRequested, StartFailed, Running, Exited
     };
 
     virtual void handleChannelSuccess();
@@ -60,9 +60,6 @@ public:
 
     virtual void closeHook();
 
-    QByteArray readAllStandardOutput();
-    QByteArray readAllStandardError();
-
 signals:
     void started();
     void readyReadStandardOutput();
@@ -76,7 +73,7 @@ private:
         SshRemoteProcess *proc);
 
     virtual void handleOpenSuccessInternal();
-    virtual void handleOpenFailureInternal();
+    virtual void handleOpenFailureInternal(const QString &reason);
     virtual void handleChannelDataInternal(const QByteArray &data);
     virtual void handleChannelExtendedDataInternal(quint32 type,
         const QByteArray &data);
diff --git a/src/plugins/debugger/gdb/remotegdbprocess.cpp b/src/plugins/debugger/gdb/remotegdbprocess.cpp
index 80a1dd583739ac5f29cd008e5b09e4772e24c058..e9bb37aefe04e3b397f2a1fd5173b0dfe7ec2bf1 100644
--- a/src/plugins/debugger/gdb/remotegdbprocess.cpp
+++ b/src/plugins/debugger/gdb/remotegdbprocess.cpp
@@ -341,7 +341,7 @@ void RemoteGdbProcess::sendInput(const QByteArray &data)
         if (!isdigit(data.at(pos)))
             break;
     m_lastSeqNr = data.left(pos);
-    m_gdbProc->sendInput(data);
+    m_gdbProc->write(data);
 }
 
 void RemoteGdbProcess::handleAppOutput()
diff --git a/src/plugins/debugger/lldb/lldbenginehost.cpp b/src/plugins/debugger/lldb/lldbenginehost.cpp
index 70189a5139321d5339323d73462467c15fd39cdf..d5115bbd6addbd57c50cc958cbd5dfb0fc46bb6c 100644
--- a/src/plugins/debugger/lldb/lldbenginehost.cpp
+++ b/src/plugins/debugger/lldb/lldbenginehost.cpp
@@ -95,7 +95,7 @@ qint64 SshIODevice::writeData (const char * data, qint64 maxSize)
         startupbuffer += QByteArray::fromRawData(data, maxSize);
         return maxSize;
     }
-    proc->sendInput(QByteArray::fromRawData(data, maxSize));
+    proc->write(data, maxSize);
     return maxSize;
 }
 qint64 SshIODevice::readData (char * data, qint64 maxSize)
@@ -128,7 +128,7 @@ qint64 SshIODevice::readData (char * data, qint64 maxSize)
 void SshIODevice::processStarted()
 {
     proc = runner->process();
-    proc->sendInput(startupbuffer);
+    proc->write(startupbuffer);
 }
 
 void SshIODevice::outputAvailable(const QByteArray &output)
diff --git a/src/plugins/madde/maddedevicetester.cpp b/src/plugins/madde/maddedevicetester.cpp
index a275971f4e710101c3f3a203f2f5e71f71ea7727..80090f07d691a0861e117e101d0232df981ecad5 100644
--- a/src/plugins/madde/maddedevicetester.cpp
+++ b/src/plugins/madde/maddedevicetester.cpp
@@ -89,7 +89,7 @@ void MaddeDeviceTester::stopTest()
     case QtTest:
     case MadDeveloperTest:
     case QmlToolingTest:
-        m_processRunner->process()->closeChannel();
+        m_processRunner->process()->close();
         break;
     }
 
diff --git a/src/plugins/madde/maemopublisherfremantlefree.cpp b/src/plugins/madde/maemopublisherfremantlefree.cpp
index aa85f8138bff9cdbafa5b17d2fdefd5257e75381..e0cf2f70432012e3db30a32a868ef9e887ef7101 100644
--- a/src/plugins/madde/maemopublisherfremantlefree.cpp
+++ b/src/plugins/madde/maemopublisherfremantlefree.cpp
@@ -447,7 +447,7 @@ void MaemoPublisherFremantleFree::prepareToSendFile()
     emit progressReport(tr("Uploading file %1 ...")
         .arg(QDir::toNativeSeparators(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');
 }
 
@@ -473,13 +473,13 @@ void MaemoPublisherFremantleFree::sendFile()
                 tr("Upload failed."));
             return;
         }
-        m_uploader->process()->sendInput(data);
+        m_uploader->process()->write(data);
         bytesToSend -= data.size();
         QCoreApplication::processEvents();
         if (m_state == Inactive)
             return;
     }
-    m_uploader->process()->sendInput(QByteArray(1, '\0'));
+    m_uploader->process()->write(QByteArray(1, '\0'));
 }
 
 void MaemoPublisherFremantleFree::handleScpStdOut(const QByteArray &output)
diff --git a/src/plugins/madde/maemoremotemounter.cpp b/src/plugins/madde/maemoremotemounter.cpp
index a151feaf3d46454bfa96890f1bd1bd3c2c1bf474..6144036243c2c5c384ac4a6b4fa3d0899d7cdb0e 100644
--- a/src/plugins/madde/maemoremotemounter.cpp
+++ b/src/plugins/madde/maemoremotemounter.cpp
@@ -380,11 +380,11 @@ void MaemoRemoteMounter::setState(State newState)
         m_utfsServerTimer->stop();
         if (m_mountProcess) {
             disconnect(m_mountProcess.data(), 0, this, 0);
-            m_mountProcess->closeChannel();
+            m_mountProcess->close();
         }
         if (m_unmountProcess) {
             disconnect(m_unmountProcess.data(), 0, this, 0);
-            m_unmountProcess->closeChannel();
+            m_unmountProcess->close();
         }
     }
     m_state = newState;
diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp
index 8c9e1cd2b9ff93bee2d1e66b0966f73f294f99e0..cd9c46517482f0b562667a8a74bd9aad00a2faa5 100644
--- a/src/plugins/remotelinux/linuxdevicetester.cpp
+++ b/src/plugins/remotelinux/linuxdevicetester.cpp
@@ -106,7 +106,7 @@ void GenericLinuxDeviceTester::stopTest()
         d->portsGatherer.stop();
         break;
     case RunningUname:
-        d->process->closeChannel();
+        d->process->close();
         break;
     case Inactive:
         break;
diff --git a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp
index 9f393b8924714292526eefddd03b244557e0b66e..d489d550300c773b4af16558d9565125b89345c0 100644
--- a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp
+++ b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp
@@ -115,7 +115,7 @@ void RemoteLinuxCustomCommandDeployService::stopDeployment()
     QTC_ASSERT(d->state == Running, return);
 
     disconnect(d->runner, 0, this, 0);
-    d->runner->process()->closeChannel();
+    d->runner->process()->close();
     d->state = Inactive;
     handleDeploymentDone();
 }
diff --git a/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp b/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp
index d4a52340c755236ab67d5ee9bbd5d9ddab8b5f9e..dd8cfafdef6127d2923e476fe6c1dbc73a77db18 100644
--- a/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp
+++ b/tests/manual/ssh/remoteprocess/remoteprocesstest.cpp
@@ -35,6 +35,7 @@
 #include <utils/ssh/sshpseudoterminal.h>
 
 #include <QtCore/QCoreApplication>
+#include <QtCore/QTextStream>
 #include <QtCore/QTimer>
 
 #include <iostream>
@@ -75,9 +76,10 @@ void RemoteProcessTest::run()
 
 void RemoteProcessTest::handleConnectionError()
 {
-    std::cerr << "Error: Connection failure ("
-        << qPrintable(m_remoteRunner->lastConnectionErrorString()) << ")."
-        << std::endl;
+    const QString error = m_state == TestingIoDevice
+        ? m_sshConnection->errorString() : m_remoteRunner->lastConnectionErrorString();
+
+    std::cerr << "Error: Connection failure (" << qPrintable(error) << ")." << std::endl;
     qApp->quit();
 }
 
@@ -92,6 +94,11 @@ void RemoteProcessTest::handleProcessStarted()
             Utils::SshRemoteProcessRunner * const killer
                 = new Utils::SshRemoteProcessRunner(this);
             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();
                 return;
             }
-            std::cout << "Ok.\nAll tests succeeded." << std::endl;
-            qApp->quit();
+            std::cout << "Ok.\nTesting I/O device functionality... " << std::flush;
+            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;
         }
+        case TestingIoDevice:
         case Inactive:
             Q_ASSERT(false);
         }
@@ -216,16 +229,23 @@ void RemoteProcessTest::handleProcessClosed(int exitStatus)
         qApp->quit();
         break;
     case SshRemoteProcess::KilledBySignal:
-        if (m_state != TestingCrash) {
+        switch (m_state) {
+        case TestingCrash:
+            std::cout << "Ok.\nTesting remote process with terminal... " << std::flush;
+            m_state = TestingTerminal;
+            m_started = false;
+            m_timeoutTimer->start();
+            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;
         }
-        std::cout << "Ok.\nTesting remote process with terminal... " << std::flush;
-        m_state = TestingTerminal;
-        m_started = false;
-        m_timeoutTimer->start();
-        m_remoteRunner->runInTerminal("top -n 1", SshPseudoTerminal(), m_sshParams);
     }
 }
 
@@ -234,3 +254,35 @@ void RemoteProcessTest::handleTimeout()
     std::cerr << "Error: Timeout waiting for progress." << std::endl;
     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);
+}
diff --git a/tests/manual/ssh/remoteprocess/remoteprocesstest.h b/tests/manual/ssh/remoteprocess/remoteprocesstest.h
index 46a20dd5b6714d38466242e8cf9d35ce2d623c60..bc1a38199902a3172ca802cfe2dcd66419f3bdc0 100644
--- a/tests/manual/ssh/remoteprocess/remoteprocesstest.h
+++ b/tests/manual/ssh/remoteprocess/remoteprocesstest.h
@@ -35,9 +35,11 @@
 
 #include <utils/ssh/sshremoteprocessrunner.h>
 
-QT_FORWARD_DECLARE_CLASS(QTimer);
 #include <QtCore/QObject>
 
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+QT_FORWARD_DECLARE_CLASS(QTimer)
+
 class RemoteProcessTest : public QObject
 {
     Q_OBJECT
@@ -53,13 +55,22 @@ private slots:
     void handleProcessStderr(const QByteArray &output);
     void handleProcessClosed(int exitStatus);
     void handleTimeout();
+    void handleReadyRead();
+    void handleConnected();
 
 private:
-    enum State { Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal };
+    enum State {
+        Inactive, TestingSuccess, TestingFailure, TestingCrash, TestingTerminal, TestingIoDevice
+    };
+
+    QString testString() const;
 
     const Utils::SshConnectionParameters m_sshParams;
     QTimer * const m_timeoutTimer;
+    QTextStream *m_textStream;
     Utils::SshRemoteProcessRunner * const m_remoteRunner;
+    Utils::SshRemoteProcess::Ptr m_catProcess;
+    Utils::SshConnection::Ptr m_sshConnection;
     QByteArray m_remoteStdout;
     QByteArray m_remoteStderr;
     State m_state;
diff --git a/tests/manual/ssh/shell/shell.cpp b/tests/manual/ssh/shell/shell.cpp
index 96b59c96208a6a03b60704f3020960122bf9cbcf..e363f9c52a3f5cd7b4297646bd8d0a95cdb49895 100644
--- a/tests/manual/ssh/shell/shell.cpp
+++ b/tests/manual/ssh/shell/shell.cpp
@@ -115,5 +115,5 @@ void Shell::handleChannelClosed(int exitStatus)
 
 void Shell::handleStdin()
 {
-    m_shell->sendInput(m_stdin->readLine());
+    m_shell->write(m_stdin->readLine());
 }