diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index 36c69c7ee72..cd306d0c4a3 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -4,7 +4,13 @@ Project { name: "QtcSsh" QtcLibrary { - cpp.defines: base.concat("QTCSSH_LIBRARY") + cpp.defines: { + var defines = base; + defines.push("QTCSSH_LIBRARY"); + if (project.withAutotests && !defines.contains("WITH_TESTS")) + defines.push("WITH_TESTS"); + return defines; + } cpp.enableExceptions: true Depends { name: "Qt"; submodules: ["widgets", "network" ] } diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index 5f8134e1c9a..839ee2d5431 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -403,4 +403,95 @@ SftpTransferPtr SshConnection::setupTransfer( d->connectionArgs(SshSettings::sftpFilePath()))); } +#ifdef WITH_TESTS +namespace SshTest { +const QString getHostFromEnvironment() +{ + const QString host = QString::fromLocal8Bit(qgetenv("QTC_SSH_TEST_HOST")); + if (host.isEmpty() && qEnvironmentVariableIsSet("QTC_SSH_TEST_DEFAULTS")) + return QString("127.0.0.1"); + return host; +} + +quint16 getPortFromEnvironment() +{ + const int port = qEnvironmentVariableIntValue("QTC_SSH_TEST_PORT"); + return port != 0 ? quint16(port) : 22; +} + +const QString getUserFromEnvironment() +{ + return QString::fromLocal8Bit(qgetenv("QTC_SSH_TEST_USER")); +} + +const QString getKeyFileFromEnvironment() +{ + const FilePath defaultKeyFile = FileUtils::homePath() / ".ssh/id_rsa"; + const QString keyFile = QString::fromLocal8Bit(qgetenv("QTC_SSH_TEST_KEYFILE")); + if (keyFile.isEmpty()) { + if (qEnvironmentVariableIsSet("QTC_SSH_TEST_DEFAULTS")) + return defaultKeyFile.toString(); + } + return keyFile; +} + +const QString userAtHost() +{ + QString userMidFix = getUserFromEnvironment(); + if (!userMidFix.isEmpty()) + userMidFix.append('@'); + return userMidFix + getHostFromEnvironment(); +} + +SshConnectionParameters getParameters() +{ + SshConnectionParameters params; + if (!qEnvironmentVariableIsSet("QTC_SSH_TEST_DEFAULTS")) { + params.setUserName(getUserFromEnvironment()); + params.privateKeyFile = Utils::FilePath::fromUserInput(getKeyFileFromEnvironment()); + } + params.setHost(getHostFromEnvironment()); + params.setPort(getPortFromEnvironment()); + params.timeout = 10; + params.authenticationType = !params.privateKeyFile.isEmpty() + ? QSsh::SshConnectionParameters::AuthenticationTypeSpecificKey + : QSsh::SshConnectionParameters::AuthenticationTypeAll; + return params; +} + +bool checkParameters(const QSsh::SshConnectionParameters ¶ms) +{ + if (qEnvironmentVariableIsSet("QTC_SSH_TEST_DEFAULTS")) + return true; + if (params.host().isEmpty()) { + qWarning("No hostname provided. Set QTC_SSH_TEST_HOST."); + return false; + } + if (params.userName().isEmpty()) + qWarning("No user name provided - test may fail with empty default. Set QTC_SSH_TEST_USER."); + if (params.privateKeyFile.isEmpty()) { + qWarning("No key file provided. Set QTC_SSH_TEST_KEYFILE."); + return false; + } + return true; +} + +void printSetupHelp() +{ + qInfo() << "In order to run this test properly it requires some setup (example for fedora):\n" + "1. Run a server on the host to connect to:\n" + " systemctl start sshd\n" + "2. Create your own ssh key (needed only once). For fedora it needs ecdsa type:\n" + " ssh-keygen -t ecdsa\n" + "3. Make your public key known to the server (needed only once):\n" + " ssh-copy-id -i [full path to your public key] [user@host]\n" + "4. Set the env variables before executing test:\n" + " QTC_SSH_TEST_HOST=127.0.0.1\n" + " QTC_SSH_TEST_KEYFILE=[full path to your private key]\n" + " QTC_SSH_TEST_USER=[your user name]\n"; +} + +} // namespace SshTest +#endif + } // namespace QSsh diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index 381bd463c91..a559fc13163 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -142,6 +142,19 @@ private: SshConnectionPrivate * const d; }; +#ifdef WITH_TESTS +namespace SshTest { +const QString QSSH_EXPORT getHostFromEnvironment(); +quint16 QSSH_EXPORT getPortFromEnvironment(); +const QString QSSH_EXPORT getUserFromEnvironment(); +const QString QSSH_EXPORT getKeyFileFromEnvironment(); +const QSSH_EXPORT QString userAtHost(); +SshConnectionParameters QSSH_EXPORT getParameters(); +bool QSSH_EXPORT checkParameters(const SshConnectionParameters ¶ms); +void QSSH_EXPORT printSetupHelp(); +} // namespace SshTest +#endif + } // namespace QSsh Q_DECLARE_METATYPE(QSsh::SshConnectionParameters::AuthenticationType) diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index 2c95a5ca81b..1cf68a056c7 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -41,10 +41,12 @@ using namespace Utils; namespace RemoteLinux { namespace Internal { -static const char TEST_IP[] = "127.0.0.1"; static const char TEST_DIR[] = "/tmp/testdir"; -static const FilePath baseFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) - + QString(TEST_DIR)); + +static const FilePath baseFilePath() +{ + return FilePath::fromString("ssh://" + QSsh::SshTest::userAtHost() + QString(TEST_DIR)); +} TestLinuxDeviceFactory::TestLinuxDeviceFactory() : IDeviceFactory("test") @@ -54,11 +56,9 @@ TestLinuxDeviceFactory::TestLinuxDeviceFactory() setConstructionFunction(&LinuxDevice::create); setCreator([] { LinuxDevice::Ptr newDev = LinuxDevice::create(); - qDebug() << "device : " << newDev->type(); newDev->setType("test"); - QSsh::SshConnectionParameters sshParams = newDev->sshParameters(); - sshParams.setHost(TEST_IP); - sshParams.setPort(22); + qDebug() << "device : " << newDev->type(); + QSsh::SshConnectionParameters sshParams = QSsh::SshTest::getParameters(); newDev->setSshParameters(sshParams); return newDev; }); @@ -66,15 +66,28 @@ TestLinuxDeviceFactory::TestLinuxDeviceFactory() FilePath createFile(const QString &name) { - FilePath testFilePath = baseFilePath / name; - FilePath dummyFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/dev/null"); + FilePath testFilePath = baseFilePath() / name; + FilePath dummyFilePath = FilePath::fromString("ssh://" + QSsh::SshTest::userAtHost() + "/dev/null"); dummyFilePath.copyFile(testFilePath); return testFilePath; } void FileSystemAccessTest::initTestCase() { - FilePath filePath = baseFilePath; + const QSsh::SshConnectionParameters params = QSsh::SshTest::getParameters(); + qDebug() << "Using following SSH parameter:" + << "\nHost:" << params.host() + << "\nPort:" << params.port() + << "\nUser:" << params.userName() + << "\nSSHKey:" << params.privateKeyFile; + if (!QSsh::SshTest::checkParameters(params)) { + m_skippedAtWhole = true; + QSsh::SshTest::printSetupHelp(); + QSKIP("Ensure you have added your default ssh public key to your own authorized keys and " + "environment QTC_REMOTELINUX_SSH_DEFAULTS set or follow setup help above."); + return; + } + FilePath filePath = baseFilePath(); if (DeviceManager::deviceForPath(filePath) == nullptr) { DeviceManager *const devMgr = DeviceManager::instance(); @@ -87,13 +100,15 @@ void FileSystemAccessTest::initTestCase() void FileSystemAccessTest::cleanupTestCase() { - QVERIFY(baseFilePath.exists()); - QVERIFY(baseFilePath.removeRecursively()); + if (m_skippedAtWhole) // no need to clean up either + return; + QVERIFY(baseFilePath().exists()); + QVERIFY(baseFilePath().removeRecursively()); } void FileSystemAccessTest::testDirStatuses() { - FilePath filePath = baseFilePath; + FilePath filePath = baseFilePath(); QVERIFY(filePath.exists()); QVERIFY(filePath.isDir()); QVERIFY(filePath.isWritableDir()); @@ -120,7 +135,7 @@ void FileSystemAccessTest::testDirStatuses() void FileSystemAccessTest::testBytesAvailable() { - FilePath testFilePath = FilePath::fromString("ssh://" + QString(TEST_IP) + "/tmp"); + FilePath testFilePath = FilePath::fromString("ssh://" + QSsh::SshTest::userAtHost() + "/tmp"); QVERIFY(testFilePath.exists()); QVERIFY(testFilePath.bytesAvailable() > 0); } @@ -143,9 +158,9 @@ void FileSystemAccessTest::testFileActions() // ToDo: remove ".contains", make fileContents exact equal content QVERIFY(testFilePath.fileContents().contains(content)); - QVERIFY(testFilePath.renameFile(baseFilePath / "test1")); + QVERIFY(testFilePath.renameFile(baseFilePath() / "test1")); // It is Ok that FilePath doesn't change itself after rename. - FilePath newTestFilePath = baseFilePath / "test1"; + FilePath newTestFilePath = baseFilePath() / "test1"; QVERIFY(newTestFilePath.exists()); QVERIFY(!testFilePath.removeFile()); QVERIFY(newTestFilePath.exists()); diff --git a/src/plugins/remotelinux/filesystemaccess_test.h b/src/plugins/remotelinux/filesystemaccess_test.h index 4b43e202d44..7dacd1ae936 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.h +++ b/src/plugins/remotelinux/filesystemaccess_test.h @@ -53,6 +53,7 @@ private slots: private: TestLinuxDeviceFactory m_testLinuxDeviceFactory; + bool m_skippedAtWhole = false; }; } // Internal diff --git a/tests/auto/ssh/CMakeLists.txt b/tests/auto/ssh/CMakeLists.txt index 2156e6d59aa..8dd1915805f 100644 --- a/tests/auto/ssh/CMakeLists.txt +++ b/tests/auto/ssh/CMakeLists.txt @@ -3,6 +3,7 @@ file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_L add_qtc_test(tst_ssh DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\"" + WITH_TESTS DEPENDS Utils QtcSsh SOURCES tst_ssh.cpp ) diff --git a/tests/auto/ssh/ssh.qbs b/tests/auto/ssh/ssh.qbs index 3f513cca778..c4f4fe38c09 100644 --- a/tests/auto/ssh/ssh.qbs +++ b/tests/auto/ssh/ssh.qbs @@ -11,6 +11,7 @@ QtcAutotest { qtc.ide_libexec_path); var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath); defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"'); + defines.push("WITH_TESTS"); return defines; } } diff --git a/tests/auto/ssh/tst_ssh.cpp b/tests/auto/ssh/tst_ssh.cpp index b4cf76a6911..df26047d5bd 100644 --- a/tests/auto/ssh/tst_ssh.cpp +++ b/tests/auto/ssh/tst_ssh.cpp @@ -47,73 +47,8 @@ #include -/* -In order to run this test properly it requires some setup (example for fedora): -1. Run a server: - systemctl start sshd -2. Create your own ssh key (needed only once). For fedora it needs ecdsa type: - ssh-keygen -t ecdsa -3. Make your public key known to the server (needed only once): - ssh-copy-id -i [full path to your public key] -4. Set the env variables before executing test: - QTC_SSH_TEST_HOST=127.0.0.1 - QTC_SSH_TEST_KEYFILE=[full path to your private key] - QTC_SSH_TEST_USER=[your user name] -*/ using namespace QSsh; -static QString getHostFromEnvironment() -{ - return QString::fromLocal8Bit(qgetenv("QTC_SSH_TEST_HOST")); -} - -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() -{ - const int port = qEnvironmentVariableIntValue(portVar()); - return port != 0 ? quint16(port) : 22; -} - -static QString getUserFromEnvironment() -{ - return QString::fromLocal8Bit(qgetenv(userVar())); -} - -static QString getKeyFileFromEnvironment() -{ - return QString::fromLocal8Bit(qgetenv(keyFileVar())); -} - -static SshConnectionParameters getParameters() -{ - SshConnectionParameters params; - params.setHost(getHostFromEnvironment()); - params.setPort(getPortFromEnvironment()); - params.setUserName(getUserFromEnvironment()); - params.timeout = 10; - params.privateKeyFile = Utils::FilePath::fromUserInput(getKeyFileFromEnvironment()); - params.authenticationType = !params.privateKeyFile.isEmpty() - ? SshConnectionParameters::AuthenticationTypeSpecificKey - : SshConnectionParameters::AuthenticationTypeAll; - return params; -} - -#define CHECK_PARAMS(params) \ - do { \ - if (params.host().isEmpty()) { \ - QSKIP("No hostname provided. Set QTC_SSH_TEST_HOST."); \ - } \ - if (params.userName().isEmpty()) \ - QSKIP(qPrintable(QString::fromLatin1("No user name provided. Set %1.") \ - .arg(QString::fromUtf8(userVar())))); \ - if (params.privateKeyFile.isEmpty()) \ - QSKIP(qPrintable(QString::fromLatin1("No key file provided. Set %1.") \ - .arg(QString::fromUtf8(keyFileVar())))); \ - } while (false) - class tst_Ssh : public QObject { Q_OBJECT @@ -137,6 +72,10 @@ private: void tst_Ssh::initTestCase() { + const SshConnectionParameters params = SshTest::getParameters(); + if (!SshTest::checkParameters(params)) + SshTest::printSetupHelp(); + Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() @@ -154,10 +93,10 @@ void tst_Ssh::errorHandling_data() QTest::newRow("no host") << QString("hgdfxgfhgxfhxgfchxgcf") << quint16(12345) << SshConnectionParameters::AuthenticationTypeAll << QString("egal") << QString(); - const QString theHost = getHostFromEnvironment(); + const QString theHost = SshTest::getHostFromEnvironment(); if (theHost.isEmpty()) return; - const quint16 thePort = getPortFromEnvironment(); + const quint16 thePort = SshTest::getPortFromEnvironment(); QTest::newRow("non-existing key file") << theHost << thePort << SshConnectionParameters::AuthenticationTypeSpecificKey << QString("root") << QString("somefilenamethatwedontexpecttocontainavalidkey"); @@ -228,8 +167,9 @@ void tst_Ssh::remoteProcess_data() void tst_Ssh::remoteProcess() { - const SshConnectionParameters params = getParameters(); - CHECK_PARAMS(params); + const SshConnectionParameters params = SshTest::getParameters(); + if (!SshTest::checkParameters(params)) + QSKIP("Insufficient setup - set QTC_SSH_TEST_* variables."); QFETCH(QByteArray, commandLine); QFETCH(bool, isBlocking); @@ -287,8 +227,9 @@ void tst_Ssh::remoteProcess() void tst_Ssh::remoteProcessChannels() { - const SshConnectionParameters params = getParameters(); - CHECK_PARAMS(params); + const SshConnectionParameters params = SshTest::getParameters(); + if (!SshTest::checkParameters(params)) + QSKIP("Insufficient setup - set QTC_SSH_TEST_* variables."); SshConnection connection(params); QVERIFY(waitForConnection(connection)); @@ -325,8 +266,9 @@ void tst_Ssh::remoteProcessChannels() void tst_Ssh::remoteProcessInput() { - const SshConnectionParameters params = getParameters(); - CHECK_PARAMS(params); + const SshConnectionParameters params = SshTest::getParameters(); + if (!SshTest::checkParameters(params)) + QSKIP("Insufficient setup - set QTC_SSH_TEST_* variables."); SshConnection connection(params); QVERIFY(waitForConnection(connection)); @@ -369,8 +311,9 @@ void tst_Ssh::remoteProcessInput() void tst_Ssh::sftp() { // Connect to server - const SshConnectionParameters params = getParameters(); - CHECK_PARAMS(params); + const SshConnectionParameters params = SshTest::getParameters(); + if (!SshTest::checkParameters(params)) + QSKIP("Insufficient setup - set QTC_SSH_TEST_* variables."); SshConnection connection(params); QVERIFY(waitForConnection(connection));