From 66c5b1e11e1f80c4113bea940302ea79926a0c13 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 4 Mar 2022 13:44:20 +0100 Subject: [PATCH] RemoteLinux: Avoid unexpected password dialog in tests Make the test depend on environment variables and give some hint how to run the test correctly. Beside this make the test work on Windows and share the setup with ssh unit test. Change-Id: I6bbf1ec863449512646ca2c51d13fec537beedbc Reviewed-by: hjk --- src/libs/ssh/ssh.qbs | 8 +- src/libs/ssh/sshconnection.cpp | 91 ++++++++++++++++++ src/libs/ssh/sshconnection.h | 13 +++ .../remotelinux/filesystemaccess_test.cpp | 47 ++++++---- .../remotelinux/filesystemaccess_test.h | 1 + tests/auto/ssh/CMakeLists.txt | 1 + tests/auto/ssh/ssh.qbs | 1 + tests/auto/ssh/tst_ssh.cpp | 93 ++++--------------- 8 files changed, 163 insertions(+), 92 deletions(-) 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));