aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2018-11-23 11:07:57 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2018-12-13 15:10:11 +0000
commitd7178b88c4b2572fb83b28f8178940766216deed (patch)
tree861eb8069fb97c8e8e79f56cb8f88f05126639fc /tests
parent030d4d01084b04af361f07dd6360dfad8e2cc19c (diff)
SSH: Use OpenSSH tools
... instead of our own SSH library. Advantages: - Full compatibility with OpenSSH behavior guaranteed. - Minimal maintenance effort. - Less code to build. - Big chunk of 3rd party sources can be removed from our repository. One the downside, Windows users now need to install OpenSSH for RemoteLinux support. Hoewever, people doing embedded development probably have it installed anyway. [ChangeLog] Switched SSH backend to OpenSSH Fixes: QTCREATORBUG-15744 Fixes: QTCREATORBUG-15807 Fixes: QTCREATORBUG-19306 Fixes: QTCREATORBUG-20210 Change-Id: Ifcfefdd39401e45ba1f4aca35d2c5bf7046c7aab Reviewed-by: Eike Ziller <eike.ziller@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/ssh/tst_ssh.cpp617
1 files changed, 154 insertions, 463 deletions
diff --git a/tests/auto/ssh/tst_ssh.cpp b/tests/auto/ssh/tst_ssh.cpp
index 03cd1b2a95..22e7062fde 100644
--- a/tests/auto/ssh/tst_ssh.cpp
+++ b/tests/auto/ssh/tst_ssh.cpp
@@ -23,23 +23,17 @@
**
****************************************************************************/
-#include <ssh/sftpchannel.h>
+#include <ssh/sftpsession.h>
+#include <ssh/sftptransfer.h>
#include <ssh/sshconnection.h>
-#include <ssh/sshdirecttcpiptunnel.h>
-#include <ssh/sshforwardedtcpiptunnel.h>
-#include <ssh/sshpseudoterminal.h>
#include <ssh/sshremoteprocessrunner.h>
-#include <ssh/sshtcpipforwardserver.h>
-#include <ssh/sshx11displayinfo_p.h>
-#include <ssh/sshx11inforetriever_p.h>
#include <utils/environment.h>
+#include <utils/temporarydirectory.h>
#include <QDateTime>
#include <QDir>
#include <QEventLoop>
#include <QStringList>
-#include <QTcpServer>
-#include <QTcpSocket>
#include <QTemporaryDir>
#include <QTimer>
#include <QtTest>
@@ -53,91 +47,51 @@ static QString getHostFromEnvironment()
return QString::fromLocal8Bit(qgetenv("QTC_SSH_TEST_HOST"));
}
-enum class TestType { Normal, Tunnel };
-static const char *portVar(TestType testType)
-{
- return testType == TestType::Normal ? "QTC_SSH_TEST_PORT" : "QTC_SSH_TEST_PORT_TUNNEL";
-}
-static const char *userVar(TestType testType)
-{
- return testType == TestType::Normal ? "QTC_SSH_TEST_USER" : "QTC_SSH_TEST_USER_TUNNEL";
-}
-static const char *pwdVar(TestType testType)
-{
- return testType == TestType::Normal ? "QTC_SSH_TEST_PASSWORD" : "QTC_SSH_TEST_PASSWORD_TUNNEL";
-}
-static const char *keyFileVar(TestType testType)
-{
- return testType == TestType::Normal ? "QTC_SSH_TEST_KEYFILE" : "QTC_SSH_TEST_KEYFILE_TUNNEL";
-}
-
-static bool canUseFallbackValue(TestType testType)
-{
- return testType == TestType::Tunnel && getHostFromEnvironment() == "localhost";
-}
+static const char *portVar() { return "QTC_SSH_TEST_PORT"; }
+static const char *userVar() { return "QTC_SSH_TEST_USER"; }
+static const char *keyFileVar() { return "QTC_SSH_TEST_KEYFILE"; }
-static quint16 getPortFromEnvironment(TestType testType)
+static quint16 getPortFromEnvironment()
{
- const int port = qEnvironmentVariableIntValue(portVar(testType));
- if (port != 0)
- return port;
- if (canUseFallbackValue(testType))
- return getPortFromEnvironment(TestType::Normal);
- return 22;
+ const int port = qEnvironmentVariableIntValue(portVar());
+ return port != 0 ? quint16(port) : 22;
}
-static QString getUserFromEnvironment(TestType testType)
+static QString getUserFromEnvironment()
{
- const QString user = QString::fromLocal8Bit(qgetenv(userVar(testType)));
- if (user.isEmpty() && canUseFallbackValue(testType))
- return getUserFromEnvironment(TestType::Normal);
- return user;
+ return QString::fromLocal8Bit(qgetenv(userVar()));
}
-static QString getPasswordFromEnvironment(TestType testType)
+static QString getKeyFileFromEnvironment()
{
- const QString pwd = QString::fromLocal8Bit(qgetenv(pwdVar(testType)));
- if (pwd.isEmpty() && canUseFallbackValue(testType))
- return getPasswordFromEnvironment(TestType::Normal);
- return pwd;
+ return QString::fromLocal8Bit(qgetenv(keyFileVar()));
}
-static QString getKeyFileFromEnvironment(TestType testType)
-{
- const QString keyFile = QString::fromLocal8Bit(qgetenv(keyFileVar(testType)));
- if (keyFile.isEmpty() && canUseFallbackValue(testType))
- return getKeyFileFromEnvironment(TestType::Normal);
- return keyFile;
-}
-
-static SshConnectionParameters getParameters(TestType testType)
+static SshConnectionParameters getParameters()
{
SshConnectionParameters params;
- params.setHost(testType == TestType::Tunnel ? QString("localhost")
- : getHostFromEnvironment());
- params.setPort(getPortFromEnvironment(testType));
- params.setUserName(getUserFromEnvironment(testType));
- params.setPassword(getPasswordFromEnvironment(testType));
+ params.setHost(getHostFromEnvironment());
+ params.setPort(getPortFromEnvironment());
+ params.setUserName(getUserFromEnvironment());
params.timeout = 10;
- params.privateKeyFile = getKeyFileFromEnvironment(testType);
- params.authenticationType = !params.password().isEmpty()
- ? SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
- : SshConnectionParameters::AuthenticationTypePublicKey;
+ params.privateKeyFile = getKeyFileFromEnvironment();
+ params.authenticationType = !params.privateKeyFile.isEmpty()
+ ? SshConnectionParameters::AuthenticationTypeSpecificKey
+ : SshConnectionParameters::AuthenticationTypeAll;
return params;
}
-#define CHECK_PARAMS(params, testType) \
+#define CHECK_PARAMS(params) \
do { \
if (params.host().isEmpty()) { \
- Q_ASSERT(testType == TestType::Normal); \
QSKIP("No hostname provided. Set QTC_SSH_TEST_HOST."); \
} \
if (params.userName().isEmpty()) \
QSKIP(qPrintable(QString::fromLatin1("No user name provided. Set %1.") \
- .arg(userVar(testType)))); \
- if (params.password().isEmpty() && params.privateKeyFile.isEmpty()) \
- QSKIP(qPrintable(QString::fromLatin1("No authentication data provided. " \
- "Set %1 or %2.").arg(pwdVar(testType), keyFileVar(testType)))); \
+ .arg(userVar()))); \
+ if (params.privateKeyFile.isEmpty()) \
+ QSKIP(qPrintable(QString::fromLatin1("No key file provided. Set %1.") \
+ .arg(keyFileVar()))); \
} while (false)
class tst_Ssh : public QObject
@@ -145,156 +99,45 @@ class tst_Ssh : public QObject
Q_OBJECT
private slots:
- void directTunnel();
+ void initTestCase();
+
void errorHandling_data();
void errorHandling();
- void forwardTunnel();
void pristineConnectionObject();
void remoteProcess_data();
void remoteProcess();
void remoteProcessChannels();
void remoteProcessInput();
void sftp();
- void x11InfoRetriever_data();
- void x11InfoRetriever();
private:
bool waitForConnection(SshConnection &connection);
};
-void tst_Ssh::directTunnel()
+void tst_Ssh::initTestCase()
{
- // Establish SSH connection
- const SshConnectionParameters params = getParameters(TestType::Tunnel);
- CHECK_PARAMS(params, TestType::Tunnel);
- SshConnection connection(params);
- QVERIFY(waitForConnection(connection));
-
- // Set up the tunnel
- QTcpServer targetServer;
- QTcpSocket *targetSocket = nullptr;
- bool tunnelInitialized = false;
- QVERIFY2(targetServer.listen(QHostAddress::LocalHost), qPrintable(targetServer.errorString()));
- const quint16 targetPort = targetServer.serverPort();
- const SshDirectTcpIpTunnel::Ptr tunnel
- = connection.createDirectTunnel("localhost", 1024, "localhost", targetPort);
- QEventLoop loop;
- const auto connectionHandler = [&targetServer, &targetSocket, &loop, &tunnelInitialized] {
- targetSocket = targetServer.nextPendingConnection();
- targetServer.close();
- if (tunnelInitialized)
- loop.quit();
- };
- connect(&targetServer, &QTcpServer::newConnection, connectionHandler);
- connect(tunnel.data(), &SshDirectTcpIpTunnel::error, &loop, &QEventLoop::quit);
- connect(tunnel.data(), &SshDirectTcpIpTunnel::initialized,
- [&tunnelInitialized, &targetSocket, &loop] {
- tunnelInitialized = true;
- if (targetSocket)
- loop.quit();
- });
- QTimer timer;
- QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- timer.setSingleShot(true);
- timer.setInterval((params.timeout + 5) * 1000);
- timer.start();
- QVERIFY(!tunnel->isOpen());
- tunnel->initialize();
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(tunnel->isOpen());
- QVERIFY(targetSocket);
- QVERIFY(tunnelInitialized);
-
- // Send data through the tunnel and check that it is received by the "remote" side
- static const QByteArray testData("Urgsblubb?");
- QByteArray clientDataReceivedByServer;
- connect(targetSocket,
- static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
- &loop, &QEventLoop::quit);
- const auto socketDataHandler = [targetSocket, &clientDataReceivedByServer, &loop] {
- clientDataReceivedByServer += targetSocket->readAll();
- if (clientDataReceivedByServer == testData)
- loop.quit();
- };
- connect(targetSocket, &QIODevice::readyRead, socketDataHandler);
- timer.start();
- tunnel->write(testData);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(tunnel->isOpen());
- QVERIFY2(targetSocket->error() == QAbstractSocket::UnknownSocketError,
- qPrintable(targetSocket->errorString()));
- QCOMPARE(clientDataReceivedByServer, testData);
-
- // Send data back and check that it is received by the "local" side
- QByteArray serverDataReceivedByClient;
- connect(tunnel.data(), &QIODevice::readyRead, [tunnel, &serverDataReceivedByClient, &loop] {
- serverDataReceivedByClient += tunnel->readAll();
- if (serverDataReceivedByClient == testData)
- loop.quit();
- });
- timer.start();
- targetSocket->write(clientDataReceivedByServer);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(tunnel->isOpen());
- QVERIFY2(targetSocket->error() == QAbstractSocket::UnknownSocketError,
- qPrintable(targetSocket->errorString()));
- QCOMPARE(serverDataReceivedByClient, testData);
-
- // Close tunnel by closing the "remote" socket
- connect(tunnel.data(), &QIODevice::aboutToClose, &loop, &QEventLoop::quit);
- timer.start();
- targetSocket->close();
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(!tunnel->isOpen());
- QVERIFY2(targetSocket->error() == QAbstractSocket::UnknownSocketError,
- qPrintable(targetSocket->errorString()));
+ Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath()
+ + "/qtc-ssh-autotest-XXXXXX");
}
-using ErrorList = QList<SshError>;
void tst_Ssh::errorHandling_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<quint16>("port");
QTest::addColumn<SshConnectionParameters::AuthenticationType>("authType");
QTest::addColumn<QString>("user");
- QTest::addColumn<QString>("password");
QTest::addColumn<QString>("keyFile");
- QTest::addColumn<ErrorList>("expectedErrors");
QTest::newRow("no host")
<< QString("hgdfxgfhgxfhxgfchxgcf") << quint16(12345)
- << SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
- << QString() << QString() << QString() << ErrorList{SshSocketError, SshTimeoutError};
+ << SshConnectionParameters::AuthenticationTypeAll << QString("egal") << QString();
const QString theHost = getHostFromEnvironment();
if (theHost.isEmpty())
return;
- const quint16 thePort = getPortFromEnvironment(TestType::Normal);
- QTest::newRow("no user")
- << theHost << thePort
- << SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
- << QString("dumdidumpuffpuff") << QString("whatever") << QString()
- << ErrorList{SshAuthenticationError};
- QTest::newRow("wrong password")
- << theHost << thePort
- << SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods
- << QString("root") << QString("thiscantpossiblybeapasswordcanit") << QString()
- << ErrorList{SshAuthenticationError};
+ const quint16 thePort = getPortFromEnvironment();
QTest::newRow("non-existing key file")
- << theHost << thePort
- << SshConnectionParameters::AuthenticationTypePublicKey
- << QString("root") << QString()
- << QString("somefilenamethatwedontexpecttocontainavalidkey")
- << ErrorList{SshKeyFileError};
-
- // TODO: Valid key file not known to the server
+ << theHost << thePort << SshConnectionParameters::AuthenticationTypeSpecificKey
+ << QString("root") << QString("somefilenamethatwedontexpecttocontainavalidkey");
}
void tst_Ssh::errorHandling()
@@ -303,15 +146,12 @@ void tst_Ssh::errorHandling()
QFETCH(quint16, port);
QFETCH(SshConnectionParameters::AuthenticationType, authType);
QFETCH(QString, user);
- QFETCH(QString, password);
QFETCH(QString, keyFile);
- QFETCH(ErrorList, expectedErrors);
SshConnectionParameters params;
params.setHost(host);
params.setPort(port);
params.setUserName(user);
- params.setPassword(password);
- params.timeout = 10;
+ params.timeout = 3;
params.authenticationType = authType;
params.privateKeyFile = keyFile;
SshConnection connection(params);
@@ -319,7 +159,7 @@ void tst_Ssh::errorHandling()
bool disconnected = false;
QString dataReceived;
QObject::connect(&connection, &SshConnection::connected, &loop, &QEventLoop::quit);
- QObject::connect(&connection, &SshConnection::error, &loop, &QEventLoop::quit);
+ QObject::connect(&connection, &SshConnection::errorOccurred, &loop, &QEventLoop::quit);
QObject::connect(&connection, &SshConnection::disconnected,
[&disconnected] { disconnected = true; });
QObject::connect(&connection, &SshConnection::dataAvailable,
@@ -327,128 +167,22 @@ void tst_Ssh::errorHandling()
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.setSingleShot(true);
- timer.start((params.timeout + 5) * 1000);
+ timer.start((params.timeout + 15) * 1000);
connection.connectToHost();
loop.exec();
QVERIFY(timer.isActive());
QCOMPARE(connection.state(), SshConnection::Unconnected);
- QVERIFY2(expectedErrors.contains(connection.errorState()),
- qPrintable(connection.errorString()));
+ QVERIFY(!connection.errorString().isEmpty());
QVERIFY(!disconnected);
QVERIFY2(dataReceived.isEmpty(), qPrintable(dataReceived));
}
-void tst_Ssh::forwardTunnel()
-{
- // Set up SSH connection
- const SshConnectionParameters params = getParameters(TestType::Tunnel);
- CHECK_PARAMS(params, TestType::Tunnel);
- SshConnection connection(params);
- QVERIFY(waitForConnection(connection));
-
- // Find a free port on the "remote" side and listen on it
- quint16 targetPort;
- {
- QTcpServer server;
- QVERIFY2(server.listen(QHostAddress::LocalHost), qPrintable(server.errorString()));
- targetPort = server.serverPort();
- }
- SshTcpIpForwardServer::Ptr server = connection.createForwardServer(QLatin1String("localhost"),
- targetPort);
- QEventLoop loop;
- QTimer timer;
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(server.data(), &SshTcpIpForwardServer::stateChanged, &loop, &QEventLoop::quit);
- connect(server.data(), &SshTcpIpForwardServer::error, &loop, &QEventLoop::quit);
- timer.setSingleShot(true);
- timer.setInterval((params.timeout + 5) * 1000);
- timer.start();
- QCOMPARE(server->state(), SshTcpIpForwardServer::Inactive);
- server->initialize();
- QCOMPARE(server->state(), SshTcpIpForwardServer::Initializing);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(server->state() == SshTcpIpForwardServer::Listening);
-
- // Establish a tunnel
- connect(server.data(), &QSsh::SshTcpIpForwardServer::newConnection, &loop, &QEventLoop::quit);
- QTcpSocket targetSocket;
- targetSocket.connectToHost("localhost", targetPort);
- timer.start();
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- const SshForwardedTcpIpTunnel::Ptr tunnel = server->nextPendingConnection();
- QVERIFY(!tunnel.isNull());
- QVERIFY(tunnel->isOpen());
-
- // Send data through the socket and check that we receive it through the tunnel
- static const QByteArray testData("Urgsblubb?");
- QByteArray dataReceivedOnTunnel;
- QString tunnelError;
- const auto tunnelErrorHandler = [&loop, &tunnelError](const QString &error) {
- tunnelError = error;
- loop.quit();
- };
- connect(tunnel.data(), &SshForwardedTcpIpTunnel::error, tunnelErrorHandler);
- connect(tunnel.data(), &QIODevice::readyRead, [tunnel, &dataReceivedOnTunnel, &loop] {
- dataReceivedOnTunnel += tunnel->readAll();
- if (dataReceivedOnTunnel == testData)
- loop.quit();
- });
- timer.start();
- targetSocket.write(testData);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(tunnel->isOpen());
- QVERIFY2(tunnelError.isEmpty(), qPrintable(tunnelError));
- QCOMPARE(dataReceivedOnTunnel, testData);
-
- // Send data though the tunnel and check that we receive it on the socket
- QByteArray dataReceivedOnSocket;
- connect(&targetSocket, &QTcpSocket::readyRead, [&targetSocket, &dataReceivedOnSocket, &loop] {
- dataReceivedOnSocket += targetSocket.readAll();
- if (dataReceivedOnSocket == testData)
- loop.quit();
- });
- timer.start();
- tunnel->write(dataReceivedOnTunnel);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(tunnel->isOpen());
- QCOMPARE(dataReceivedOnSocket, testData);
- QVERIFY2(tunnelError.isEmpty(), qPrintable(tunnelError));
-
- // Close the tunnel via the socket
- connect(tunnel.data(), &SshForwardedTcpIpTunnel::aboutToClose, &loop, &QEventLoop::quit);
- timer.start();
- targetSocket.close();
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(!tunnel->isOpen());
- QVERIFY2(tunnelError.isEmpty(), qPrintable(tunnelError));
- QCOMPARE(server->state(), SshTcpIpForwardServer::Listening);
-
- // Close the server
- timer.start();
- server->close();
- QCOMPARE(server->state(), SshTcpIpForwardServer::Closing);
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QCOMPARE(server->state(), SshTcpIpForwardServer::Inactive);
-}
-
void tst_Ssh::pristineConnectionObject()
{
QSsh::SshConnection connection((SshConnectionParameters()));
QCOMPARE(connection.state(), SshConnection::Unconnected);
- QVERIFY(connection.createRemoteProcess("").isNull());
- QVERIFY(connection.createSftpChannel().isNull());
+ QVERIFY(!connection.createRemoteProcess(""));
+ QVERIFY(!connection.createSftpSession());
}
void tst_Ssh::remoteProcess_data()
@@ -472,8 +206,8 @@ void tst_Ssh::remoteProcess_data()
void tst_Ssh::remoteProcess()
{
- const SshConnectionParameters params = getParameters(TestType::Normal);
- CHECK_PARAMS(params, TestType::Normal);
+ const SshConnectionParameters params = getParameters();
+ CHECK_PARAMS(params);
QFETCH(QByteArray, commandLine);
QFETCH(bool, useTerminal);
@@ -494,7 +228,7 @@ void tst_Ssh::remoteProcess()
connect(&runner, &SshRemoteProcessRunner::readyReadStandardError,
[&remoteStderr, &runner] { remoteStderr += runner.readAllStandardError(); });
if (useTerminal)
- runner.runInTerminal(commandLine, SshPseudoTerminal(), params);
+ runner.runInTerminal(commandLine, params);
else
runner.run(commandLine, params);
QTimer timer;
@@ -505,6 +239,9 @@ void tst_Ssh::remoteProcess()
loop.exec();
QVERIFY(timer.isActive());
timer.stop();
+ QVERIFY2(runner.lastConnectionErrorString().isEmpty(),
+ qPrintable(runner.lastConnectionErrorString()));
+ QVERIFY2(runner.processErrorString().isEmpty(), qPrintable(runner.processErrorString()));
QVERIFY(runner.isProcessRunning()); // Event loop exit should have been triggered by started().
QVERIFY2(remoteStdout.isEmpty(), remoteStdout.constData());
QVERIFY2(remoteStderr.isEmpty(), remoteStderr.constData());
@@ -518,12 +255,11 @@ void tst_Ssh::remoteProcess()
QVERIFY(timer.isActive());
timer.stop();
QVERIFY(!runner.isProcessRunning());
+ QVERIFY2(runner.lastConnectionErrorString().isEmpty(),
+ qPrintable(runner.lastConnectionErrorString()));
if (isBlocking) {
- // Some shells (e.g. mksh) do not report a crash exit.
- if (runner.processExitStatus() == SshRemoteProcess::CrashExit)
- QCOMPARE(runner.processExitSignal(), SshRemoteProcess::KillSignal);
- else
- QVERIFY(runner.processExitCode() != 0);
+ QVERIFY(runner.processExitStatus() == SshRemoteProcess::CrashExit
+ || runner.processExitCode() != 0);
} else {
QCOMPARE(successExpected, runner.processExitCode() == 0);
}
@@ -533,8 +269,8 @@ void tst_Ssh::remoteProcess()
void tst_Ssh::remoteProcessChannels()
{
- const SshConnectionParameters params = getParameters(TestType::Normal);
- CHECK_PARAMS(params, TestType::Normal);
+ const SshConnectionParameters params = getParameters();
+ CHECK_PARAMS(params);
SshConnection connection(params);
QVERIFY(waitForConnection(connection));
@@ -542,17 +278,17 @@ void tst_Ssh::remoteProcessChannels()
QByteArray remoteStdout;
QByteArray remoteStderr;
QByteArray remoteData;
- SshRemoteProcess::Ptr echoProcess
+ SshRemoteProcessPtr echoProcess
= connection.createRemoteProcess("printf " + testString + " >&2");
echoProcess->setReadChannel(QProcess::StandardError);
QEventLoop loop;
- connect(echoProcess.data(), &SshRemoteProcess::closed, &loop, &QEventLoop::quit);
- connect(echoProcess.data(), &QIODevice::readyRead,
- [&remoteData, echoProcess] { remoteData += echoProcess->readAll(); });
- connect(echoProcess.data(), &SshRemoteProcess::readyReadStandardOutput,
- [&remoteStdout, echoProcess] { remoteStdout += echoProcess->readAllStandardOutput(); });
- connect(echoProcess.data(), &SshRemoteProcess::readyReadStandardError,
- [&remoteStderr, echoProcess] { remoteStderr = testString; });
+ connect(echoProcess.get(), &SshRemoteProcess::done, &loop, &QEventLoop::quit);
+ connect(echoProcess.get(), &QIODevice::readyRead,
+ [&remoteData, p = echoProcess.get()] { remoteData += p->readAll(); });
+ connect(echoProcess.get(), &SshRemoteProcess::readyReadStandardOutput,
+ [&remoteStdout, p = echoProcess.get()] { remoteStdout += p->readAllStandardOutput(); });
+ connect(echoProcess.get(), &SshRemoteProcess::readyReadStandardError,
+ [&remoteStderr] { remoteStderr = testString; });
echoProcess->start();
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
@@ -563,7 +299,7 @@ void tst_Ssh::remoteProcessChannels()
QVERIFY(timer.isActive());
timer.stop();
QVERIFY(!echoProcess->isRunning());
- QCOMPARE(echoProcess->exitSignal(), SshRemoteProcess::NoSignal);
+ QCOMPARE(echoProcess->exitStatus(), QProcess::NormalExit);
QCOMPARE(echoProcess->exitCode(), 0);
QVERIFY(remoteStdout.isEmpty());
QCOMPARE(remoteData, testString);
@@ -572,16 +308,16 @@ void tst_Ssh::remoteProcessChannels()
void tst_Ssh::remoteProcessInput()
{
- const SshConnectionParameters params = getParameters(TestType::Normal);
- CHECK_PARAMS(params, TestType::Normal);
+ const SshConnectionParameters params = getParameters();
+ CHECK_PARAMS(params);
SshConnection connection(params);
QVERIFY(waitForConnection(connection));
- SshRemoteProcess::Ptr catProcess
+ SshRemoteProcessPtr catProcess
= connection.createRemoteProcess(QString::fromLatin1("/bin/cat").toUtf8());
QEventLoop loop;
- connect(catProcess.data(), &SshRemoteProcess::started, &loop, &QEventLoop::quit);
- connect(catProcess.data(), &SshRemoteProcess::closed, &loop, &QEventLoop::quit);
+ connect(catProcess.get(), &SshRemoteProcess::started, &loop, &QEventLoop::quit);
+ connect(catProcess.get(), &SshRemoteProcess::done, &loop, &QEventLoop::quit);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
timer.setSingleShot(true);
@@ -594,8 +330,8 @@ void tst_Ssh::remoteProcessInput()
QVERIFY(catProcess->isRunning());
static QString testString = "x\r\n";
- connect(catProcess.data(), &QIODevice::readyRead, &loop, &QEventLoop::quit);
- QTextStream stream(catProcess.data());
+ connect(catProcess.get(), &QIODevice::readyRead, &loop, &QEventLoop::quit);
+ QTextStream stream(catProcess.get());
stream << testString;
stream.flush();
timer.start();
@@ -611,53 +347,20 @@ void tst_Ssh::remoteProcessInput()
timer.start();
loop.exec();
QVERIFY(!catProcess->isRunning());
- QVERIFY(catProcess->exitCode() != 0
- || catProcess->exitSignal() == SshRemoteProcess::KillSignal);
+ QVERIFY(catProcess->exitCode() != 0 || catProcess->exitStatus() == QProcess::CrashExit);
}
void tst_Ssh::sftp()
{
// Connect to server
- const SshConnectionParameters params = getParameters(TestType::Normal);
- CHECK_PARAMS(params, TestType::Normal);
+ const SshConnectionParameters params = getParameters();
+ CHECK_PARAMS(params);
SshConnection connection(params);
QVERIFY(waitForConnection(connection));
- // Establish SFTP channel
- SftpChannel::Ptr sftpChannel = connection.createSftpChannel();
- QList<SftpJobId> jobs;
- bool invalidFinishedSignal = false;
- QString jobError;
- QEventLoop loop;
- connect(sftpChannel.data(), &SftpChannel::initialized, &loop, &QEventLoop::quit);
- connect(sftpChannel.data(), &SftpChannel::channelError, &loop, &QEventLoop::quit);
- connect(sftpChannel.data(), &SftpChannel::closed, &loop, &QEventLoop::quit);
- connect(sftpChannel.data(), &SftpChannel::finished,
- [&loop, &jobs, &invalidFinishedSignal, &jobError](SftpJobId job, const QString &error) {
- if (!jobs.removeOne(job)) {
- invalidFinishedSignal = true;
- loop.quit();
- return;
- }
- if (!error.isEmpty()) {
- jobError = error;
- loop.quit();
- return;
- }
- if (jobs.empty())
- loop.quit();
- });
- QTimer timer;
- QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- timer.setSingleShot(true);
- timer.setInterval((params.timeout + 5) * 1000);
- timer.start();
- sftpChannel->initialize();
- loop.exec();
- QVERIFY(timer.isActive());
- timer.stop();
- QVERIFY(!invalidFinishedSignal);
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ const SshConnectionInfo connInfo = connection.connectionInfo();
+ QVERIFY(connInfo.isValid());
+ QCOMPARE(connInfo.peerPort, params.port());
// Create and upload 1000 small files and one big file
QTemporaryDir dirForFilesToUpload;
@@ -670,6 +373,7 @@ void tst_Ssh::sftp()
const auto getDownloadFilePath = [&dirForFilesToDownload](const QString &localFileName) {
return QString(dirForFilesToDownload.path()).append('/').append(localFileName);
};
+ FilesToTransfer filesToUpload;
std::srand(QDateTime::currentDateTime().toSecsSinceEpoch());
for (int i = 0; i < 1000; ++i) {
const QString fileName = "sftptestfile" + QString::number(i + 1);
@@ -681,11 +385,7 @@ void tst_Ssh::sftp()
file.write(reinterpret_cast<char *>(content), sizeof content);
file.close();
QVERIFY2(file.error() == QFile::NoError, qPrintable(file.errorString()));
- const QString remoteFilePath = getRemoteFilePath(fileName);
- const SftpJobId uploadJob = sftpChannel->uploadFile(file.fileName(), remoteFilePath,
- SftpOverwriteExisting);
- QVERIFY(uploadJob != SftpInvalidJob);
- jobs << uploadJob;
+ filesToUpload << FileToTransfer(file.fileName(), getRemoteFilePath(fileName));
}
const QString bigFileName("sftpbigfile");
QFile bigFile(dirForFilesToUpload.path() + '/' + bigFileName);
@@ -701,16 +401,55 @@ void tst_Ssh::sftp()
}
bigFile.close();
QVERIFY2(bigFile.error() == QFile::NoError, qPrintable(bigFile.errorString()));
- const SftpJobId uploadJob = sftpChannel->uploadFile(bigFile.fileName(),
- getRemoteFilePath(bigFileName), SftpOverwriteExisting);
- QVERIFY(uploadJob != SftpInvalidJob);
- jobs << uploadJob;
- QCOMPARE(jobs.size(), 1001);
+ filesToUpload << FileToTransfer(bigFile.fileName(), getRemoteFilePath(bigFileName));
+
+ const SftpTransferPtr upload = connection.createUpload(filesToUpload,
+ FileTransferErrorHandling::Abort);
+ QString jobError;
+ QEventLoop loop;
+ connect(upload.get(), &SftpTransfer::done, [&jobError, &loop](const QString &error) {
+ jobError = error;
+ loop.quit();
+ });
+ QTimer timer;
+ QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
+ timer.setSingleShot(true);
+ timer.setInterval((params.timeout + 5) * 1000);
+ timer.start();
+ upload->start();
loop.exec();
- QVERIFY(!invalidFinishedSignal);
+ QVERIFY(timer.isActive());
+ timer.stop();
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
- QVERIFY(jobs.empty());
+
+ // Establish interactive SFTP session
+ SftpSessionPtr sftpChannel = connection.createSftpSession();
+ QList<SftpJobId> jobs;
+ bool invalidFinishedSignal = false;
+ connect(sftpChannel.get(), &SftpSession::started, &loop, &QEventLoop::quit);
+ connect(sftpChannel.get(), &SftpSession::done, &loop, &QEventLoop::quit);
+ connect(sftpChannel.get(), &SftpSession::commandFinished,
+ [&loop, &jobs, &invalidFinishedSignal, &jobError](SftpJobId job, const QString &error) {
+ if (!jobs.removeOne(job)) {
+ invalidFinishedSignal = true;
+ loop.quit();
+ return;
+ }
+ if (!error.isEmpty()) {
+ jobError = error;
+ loop.quit();
+ return;
+ }
+ if (jobs.empty())
+ loop.quit();
+ });
+ timer.start();
+ sftpChannel->start();
+ loop.exec();
+ QVERIFY(timer.isActive());
+ timer.stop();
+ QVERIFY(!invalidFinishedSignal);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
// Download the uploaded files to a different location
const QStringList allUploadedFileNames
@@ -720,8 +459,7 @@ void tst_Ssh::sftp()
const QString localFilePath = dirForFilesToUpload.path() + '/' + fileName;
const QString remoteFilePath = getRemoteFilePath(fileName);
const QString downloadFilePath = getDownloadFilePath(fileName);
- const SftpJobId downloadJob = sftpChannel->downloadFile(remoteFilePath, downloadFilePath,
- SftpOverwriteExisting);
+ const SftpJobId downloadJob = sftpChannel->downloadFile(remoteFilePath, downloadFilePath);
QVERIFY(downloadJob != SftpInvalidJob);
jobs << downloadJob;
}
@@ -729,7 +467,7 @@ void tst_Ssh::sftp()
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
// Compare contents of uploaded and downloaded files
@@ -762,7 +500,7 @@ void tst_Ssh::sftp()
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
// Create a directory on the remote system
@@ -773,7 +511,7 @@ void tst_Ssh::sftp()
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
// Retrieve and check the attributes of the remote directory
@@ -782,35 +520,37 @@ void tst_Ssh::sftp()
= [&remoteFileInfo](SftpJobId, const QList<SftpFileInfo> &fileInfoList) {
remoteFileInfo << fileInfoList;
};
- connect(sftpChannel.data(), &SftpChannel::fileInfoAvailable, fileInfoHandler);
- const SftpJobId statDirJob = sftpChannel->statFile(remoteDirPath);
+ connect(sftpChannel.get(), &SftpSession::fileInfoAvailable, fileInfoHandler);
+ const SftpJobId statDirJob = sftpChannel->ls(remoteDirPath + "/..");
QVERIFY(statDirJob != SftpInvalidJob);
jobs << statDirJob;
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
- QCOMPARE(remoteFileInfo.size(), 1);
- const SftpFileInfo remoteDirInfo = remoteFileInfo.takeFirst();
+ QVERIFY(!remoteFileInfo.empty());
+ SftpFileInfo remoteDirInfo;
+ for (const SftpFileInfo &fi : qAsConst(remoteFileInfo)) {
+ if (fi.name == QFileInfo(remoteDirPath).fileName()) {
+ remoteDirInfo = fi;
+ break;
+ }
+ }
QCOMPARE(remoteDirInfo.type, FileTypeDirectory);
QCOMPARE(remoteDirInfo.name, QFileInfo(remoteDirPath).fileName());
// Retrieve and check the contents of the remote directory
- const SftpJobId lsDirJob = sftpChannel->listDirectory(remoteDirPath);
+ remoteFileInfo.clear();
+ const SftpJobId lsDirJob = sftpChannel->ls(remoteDirPath);
QVERIFY(lsDirJob != SftpInvalidJob);
jobs << lsDirJob;
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
- QCOMPARE(remoteFileInfo.size(), 2);
- for (const SftpFileInfo &fi : remoteFileInfo) {
- QCOMPARE(fi.type, FileTypeDirectory);
- QVERIFY2(fi.name == "." || fi.name == "..", qPrintable(fi.name));
- }
- QVERIFY(remoteFileInfo.first().name != remoteFileInfo.last().name);
+ QCOMPARE(remoteFileInfo.size(), 0);
// Remove the remote directory.
const SftpJobId rmDirJob = sftpChannel->removeDirectory(remoteDirPath);
@@ -819,84 +559,35 @@ void tst_Ssh::sftp()
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Initialized);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Running);
QVERIFY(jobs.empty());
// Closing down
- sftpChannel->closeChannel();
- QCOMPARE(sftpChannel->state(), SftpChannel::Closing);
+ sftpChannel->quit();
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Closing);
loop.exec();
QVERIFY(!invalidFinishedSignal);
QVERIFY2(jobError.isEmpty(), qPrintable(jobError));
- QCOMPARE(sftpChannel->state(), SftpChannel::Closed);
-}
-
-void tst_Ssh::x11InfoRetriever_data()
-{
- QTest::addColumn<QString>("displayName");
- QTest::addColumn<bool>("successExpected");
-
- const Utils::FileName xauthCommand
- = Utils::Environment::systemEnvironment().searchInPath("xauth");
- const QString displayName = QLatin1String(qgetenv("DISPLAY"));
- const bool canSucceed = xauthCommand.exists() && !displayName.isEmpty();
- QTest::newRow(canSucceed ? "suitable host" : "unsuitable host") << displayName << canSucceed;
- QTest::newRow("invalid display name") << QString("dummy") << false;
-}
-
-void tst_Ssh::x11InfoRetriever()
-{
- QFETCH(QString, displayName);
- QFETCH(bool, successExpected);
- using namespace QSsh::Internal;
- auto * const x11InfoRetriever = new SshX11InfoRetriever(displayName);
- QEventLoop loop;
- bool success;
- X11DisplayInfo displayInfo;
- QString errorMessage;
- const auto successHandler = [&loop, &success, &displayInfo](const X11DisplayInfo &di) {
- success = true;
- displayInfo = di;
- loop.quit();
- };
- connect(x11InfoRetriever, &SshX11InfoRetriever::success, successHandler);
- const auto failureHandler = [&loop, &success, &errorMessage](const QString &error) {
- success = false;
- errorMessage = error;
- loop.quit();
- };
- connect(x11InfoRetriever, &SshX11InfoRetriever::failure, failureHandler);
- QTimer timer;
- QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- timer.setSingleShot(true);
- timer.setInterval(40000);
+ QCOMPARE(sftpChannel->state(), SftpSession::State::Inactive);
+ connect(&connection, &SshConnection::disconnected, &loop, &QEventLoop::quit);
timer.start();
- x11InfoRetriever->start();
+ connection.disconnectFromHost();
loop.exec();
QVERIFY(timer.isActive());
- timer.stop();
- if (successExpected) {
- QVERIFY2(success, qPrintable(errorMessage));
- QVERIFY(!displayInfo.protocol.isEmpty());
- QVERIFY(!displayInfo.cookie.isEmpty());
- QCOMPARE(displayInfo.cookie.size(), displayInfo.randomCookie.size());
- QCOMPARE(displayInfo.displayName, displayName);
- } else {
- QVERIFY(!success);
- QVERIFY(!errorMessage.isEmpty());
- }
+ QCOMPARE(connection.state(), SshConnection::Unconnected);
+ QVERIFY2(connection.errorString().isEmpty(), qPrintable(connection.errorString()));
}
bool tst_Ssh::waitForConnection(SshConnection &connection)
{
QEventLoop loop;
QObject::connect(&connection, &SshConnection::connected, &loop, &QEventLoop::quit);
- QObject::connect(&connection, &SshConnection::error, &loop, &QEventLoop::quit);
+ QObject::connect(&connection, &SshConnection::errorOccurred, &loop, &QEventLoop::quit);
connection.connectToHost();
loop.exec();
- if (connection.errorState() != SshNoError)
+ if (!connection.errorString().isEmpty())
qDebug() << connection.errorString();
- return connection.state() == SshConnection::Connected && connection.errorState() == SshNoError;
+ return connection.state() == SshConnection::Connected && connection.errorString().isEmpty();
}
QTEST_MAIN(tst_Ssh)