diff --git a/src/libs/ssh/CMakeLists.txt b/src/libs/ssh/CMakeLists.txt index f0722f8083d..0d38ad1de46 100644 --- a/src/libs/ssh/CMakeLists.txt +++ b/src/libs/ssh/CMakeLists.txt @@ -2,7 +2,6 @@ add_qtc_library(QtcSsh DEPENDS Qt5::Core Qt5::Network Qt5::Widgets Utils SOURCES sftpdefs.cpp sftpdefs.h - sftpsession.cpp sftpsession.h sftptransfer.cpp sftptransfer.h ssh.qrc ssh_global.h diff --git a/src/libs/ssh/sftpdefs.h b/src/libs/ssh/sftpdefs.h index e3d9be9b974..a438a1652c4 100644 --- a/src/libs/ssh/sftpdefs.h +++ b/src/libs/ssh/sftpdefs.h @@ -37,8 +37,6 @@ namespace QSsh { class SftpTransfer; using SftpTransferPtr = std::unique_ptr; -class SftpSession; -using SftpSessionPtr = std::unique_ptr; enum class FileTransferErrorHandling { Abort, Ignore }; diff --git a/src/libs/ssh/sftpsession.cpp b/src/libs/ssh/sftpsession.cpp deleted file mode 100644 index 2112475eb4f..00000000000 --- a/src/libs/ssh/sftpsession.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sftpsession.h" - -#include "sshlogging_p.h" -#include "sshremoteprocess.h" -#include "sshsettings.h" - -#include -#include -#include - -#include -#include -#include -#include - -using namespace Utils; - -namespace QSsh { -using namespace Internal; - -enum class CommandType { Ls, Mkdir, Rmdir, Rm, Rename, Ln, Put, Get, None }; - -struct Command -{ - Command() = default; - Command(CommandType t, const QStringList &p, SftpJobId id) : type(t), paths(p), jobId(id) {} - bool isValid() const { return type != CommandType::None; } - - CommandType type = CommandType::None; - QStringList paths; - SftpJobId jobId = SftpInvalidJob; -}; - -struct SftpSession::SftpSessionPrivate -{ - QtcProcess sftpProc; - QStringList connectionArgs; - QByteArray output; - QQueue pendingCommands; - Command activeCommand; - SftpJobId nextJobId = 1; - SftpSession::State state = SftpSession::State::Inactive; - - QByteArray commandString(CommandType command) const - { - switch (command) { - case CommandType::Ls: return "ls -n"; - case CommandType::Mkdir: return "mkdir"; - case CommandType::Rmdir: return "rmdir"; - case CommandType::Rm: return "rm"; - case CommandType::Rename: return "rename"; - case CommandType::Ln: return "ln -s"; - case CommandType::Put: return "put"; - case CommandType::Get: return "get"; - default: QTC_ASSERT(false, return QByteArray()); - } - } - - SftpJobId queueCommand(CommandType command, const QStringList &paths) - { - qCDebug(sshLog) << "queueing command" << int(command) << paths; - - const SftpJobId jobId = nextJobId++; - pendingCommands.enqueue(Command(command, paths, jobId)); - runNextCommand(); - return jobId; - } - - void runNextCommand() - { - if (activeCommand.isValid()) - return; - if (pendingCommands.empty()) - return; - QTC_ASSERT(sftpProc.isRunning(), return); - activeCommand = pendingCommands.dequeue(); - - // The second newline forces the prompt to appear after the command has finished. - sftpProc.write(commandString(activeCommand.type) + ' ' - + ProcessArgs::createUnixArgs(activeCommand.paths) - .toString().toLocal8Bit() + "\n\n"); - } -}; - -static QByteArray prompt() { return "sftp> "; } - -SftpSession::SftpSession(const QStringList &connectionArgs) : d(new SftpSessionPrivate) -{ - SshRemoteProcess::setupSshEnvironment(&d->sftpProc); - d->sftpProc.setProcessMode(ProcessMode::Writer); - d->connectionArgs = connectionArgs; - connect(&d->sftpProc, &QtcProcess::started, [this] { - qCDebug(sshLog) << "sftp process started"; - d->sftpProc.write("\n"); // Force initial prompt. - }); - connect(&d->sftpProc, &QtcProcess::done, [this] { - qCDebug(sshLog) << "sftp process finished"; - - d->state = State::Inactive; - const QString processMessage = [this] { - if (d->sftpProc.error() == QProcess::FailedToStart) - return tr("sftp failed to start: %1").arg(d->sftpProc.errorString()); - if (d->sftpProc.exitStatus() != QProcess::NormalExit) - return tr("sftp crashed."); - if (d->sftpProc.exitCode() != 0) - return QString::fromLocal8Bit(d->sftpProc.readAllStandardError()); - return QString(); - }(); - emit done(processMessage); - }); - connect(&d->sftpProc, &QtcProcess::readyReadStandardOutput, this, &SftpSession::handleStdout); -} - -void SftpSession::doStart() -{ - if (d->state != State::Starting) - return; - const FilePath sftpBinary = SshSettings::sftpFilePath(); - if (!sftpBinary.exists()) { - d->state = State::Inactive; - emit done(tr("Cannot establish SFTP session: sftp binary \"%1\" does not exist.") - .arg(sftpBinary.toUserOutput())); - return; - } - d->activeCommand = Command(); - const QStringList args = QStringList{"-q"} << d->connectionArgs; - qCDebug(sshLog) << "starting sftp session:" << sftpBinary.toUserOutput() << args; - d->sftpProc.setCommand(CommandLine(sftpBinary, args)); - d->sftpProc.start(); -} - -void SftpSession::handleStdout() -{ - if (state() == State::Running && !d->activeCommand.isValid()) { - qCWarning(sshLog) << "ignoring unexpected sftp output:" - << d->sftpProc.readAllStandardOutput(); - return; - } - - d->output += d->sftpProc.readAllStandardOutput(); - qCDebug(sshLog) << "accumulated sftp output:" << d->output; - const int firstPromptOffset = d->output.indexOf(prompt()); - if (firstPromptOffset == -1) - return; - if (state() == State::Starting) { - d->state = State::Running; - d->output.clear(); - d->sftpProc.readAllStandardError(); // The "connected" message goes to stderr. - emit started(); - return; - } - const int secondPromptOffset = d->output.indexOf(prompt(), firstPromptOffset + prompt().size()); - if (secondPromptOffset == -1) - return; - const Command command = d->activeCommand; - d->activeCommand = Command(); - const QByteArray commandOutput = d->output.mid( - firstPromptOffset + prompt().size(), - secondPromptOffset - firstPromptOffset - prompt().size()); - d->output = d->output.mid(secondPromptOffset + prompt().size()); - if (command.type == CommandType::Ls) - handleLsOutput(command.jobId, commandOutput); - const QByteArray stdErr = d->sftpProc.readAllStandardError(); - emit commandFinished(command.jobId, QString::fromLocal8Bit(stdErr)); - d->runNextCommand(); -} - -static SftpFileType typeFromLsOutput(char c) -{ - if (c == '-') - return FileTypeRegular; - if (c == 'd') - return FileTypeDirectory; - return FileTypeOther; -} - -static QFile::Permissions permissionsFromLsOutput(const QByteArray &output) -{ - QFile::Permissions perms; - if (output.at(0) == 'r') - perms |= QFile::ReadOwner; - if (output.at(1) == 'w') - perms |= QFile::WriteOwner; - if (output.at(2) == 'x') - perms |= QFile::ExeOwner; - if (output.at(3) == 'r') - perms |= QFile::ReadGroup; - if (output.at(4) == 'w') - perms |= QFile::WriteGroup; - if (output.at(5) == 'x') - perms |= QFile::ExeGroup; - if (output.at(6) == 'r') - perms |= QFile::ReadOther; - if (output.at(7) == 'w') - perms |= QFile::WriteOther; - if (output.at(8) == 'x') - perms |= QFile::ExeOther; - return perms; -} - -void SftpSession::handleLsOutput(SftpJobId jobId, const QByteArray &output) -{ - QList allFileInfo; - for (const QByteArray &line : output.split('\n')) { - if (line.startsWith("ls") || line.isEmpty()) - continue; - const QByteArrayList components = line.simplified().split(' '); - if (components.size() < 9) { - qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; - continue; - } - const QByteArray typeAndPermissions = components.first(); - if (typeAndPermissions.size() != 10) { - qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; - continue; - } - SftpFileInfo fileInfo; - fileInfo.type = typeFromLsOutput(typeAndPermissions.at(0)); - fileInfo.permissions = permissionsFromLsOutput(QByteArray::fromRawData( - typeAndPermissions.constData() + 1, - typeAndPermissions.size() - 1)); - bool isNumber; - fileInfo.size = components.at(4).toULongLong(&isNumber); - if (!isNumber) { - qCWarning(sshLog) << "Don't know how to parse sftp ls output:" << line; - continue; - } - // TODO: This will not work for file names with weird whitespace combinations - fileInfo.name = QFileInfo(QString::fromUtf8(components.mid(8).join(' '))).fileName(); - allFileInfo << fileInfo; - } - emit fileInfoAvailable(jobId, allFileInfo); -} - -SftpSession::~SftpSession() -{ - quit(); - delete d; -} - -void SftpSession::start() -{ - QTC_ASSERT(d->state == State::Inactive, return); - d->state = State::Starting; - QTimer::singleShot(0, this, &SftpSession::doStart); -} - -void SftpSession::quit() -{ - qCDebug(sshLog) << "quitting sftp session, current state is" << int(state()); - - switch (state()) { - case State::Starting: - case State::Closing: - d->state = State::Closing; - d->sftpProc.kill(); - break; - case State::Running: - d->state = State::Closing; - d->sftpProc.write("bye\n"); - break; - case State::Inactive: - break; - } -} - -SftpJobId SftpSession::ls(const QString &path) -{ - return d->queueCommand(CommandType::Ls, QStringList(path)); -} - -SftpJobId SftpSession::createDirectory(const QString &path) -{ - return d->queueCommand(CommandType::Mkdir, QStringList(path)); -} - -SftpJobId SftpSession::removeDirectory(const QString &path) -{ - return d->queueCommand(CommandType::Rmdir, QStringList(path)); -} - -SftpJobId SftpSession::removeFile(const QString &path) -{ - return d->queueCommand(CommandType::Rm, QStringList(path)); -} - -SftpJobId SftpSession::rename(const QString &oldPath, const QString &newPath) -{ - return d->queueCommand(CommandType::Rename, QStringList{oldPath, newPath}); -} - -SftpJobId SftpSession::createSoftLink(const QString &filePath, const QString &target) -{ - return d->queueCommand(CommandType::Ln, QStringList{filePath, target}); -} - -SftpJobId SftpSession::uploadFile(const QString &localFilePath, const QString &remoteFilePath) -{ - return d->queueCommand(CommandType::Put, QStringList{localFilePath, remoteFilePath}); -} - -SftpJobId SftpSession::downloadFile(const QString &remoteFilePath, const QString &localFilePath) -{ - return d->queueCommand(CommandType::Get, QStringList{remoteFilePath, localFilePath}); -} - -SftpSession::State SftpSession::state() const -{ - return d->state; -} - -} // namespace QSsh diff --git a/src/libs/ssh/sftpsession.h b/src/libs/ssh/sftpsession.h deleted file mode 100644 index 89d0eec145d..00000000000 --- a/src/libs/ssh/sftpsession.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "sftpdefs.h" -#include "ssh_global.h" - -#include - -namespace QSsh { -class SshConnection; - -class QSSH_EXPORT SftpSession : public QObject -{ - friend class SshConnection; - Q_OBJECT -public: - ~SftpSession() override; - void start(); - void quit(); - - SftpJobId ls(const QString &path); - SftpJobId createDirectory(const QString &path); - SftpJobId removeDirectory(const QString &path); - SftpJobId removeFile(const QString &path); - SftpJobId rename(const QString &oldPath, const QString &newPath); - SftpJobId createSoftLink(const QString &filePath, const QString &target); - SftpJobId uploadFile(const QString &localFilePath, const QString &remoteFilePath); - SftpJobId downloadFile(const QString &remoteFilePath, const QString &localFilePath); - - enum class State { Inactive, Starting, Running, Closing }; - State state() const; - -signals: - void started(); - void done(const QString &error); - void commandFinished(SftpJobId job, const QString &error); - void fileInfoAvailable(SftpJobId job, const QList &fileInfoList); - -private: - SftpSession(const QStringList &connectionArgs); - void doStart(); - void handleStdout(); - void handleLsOutput(SftpJobId jobId, const QByteArray &output); - - struct SftpSessionPrivate; - SftpSessionPrivate * const d; -}; - -} // namespace QSsh diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index 2c96156a6d9..526415a6172 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -19,8 +19,6 @@ Project { files: [ "sftpdefs.cpp", "sftpdefs.h", - "sftpsession.cpp", - "sftpsession.h", "sftptransfer.cpp", "sftptransfer.h", "ssh.qrc", diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index 60cde1181eb..d1cf80be36a 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -25,7 +25,6 @@ #include "sshconnection.h" -#include "sftpsession.h" #include "sftptransfer.h" #include "sshlogging_p.h" #include "sshremoteprocess.h" @@ -324,12 +323,6 @@ SftpTransferPtr SshConnection::createDownload(const FilesToTransfer &files, return setupTransfer(files, Internal::FileTransferType::Download, errorHandlingMode); } -SftpSessionPtr SshConnection::createSftpSession() -{ - QTC_ASSERT(state() == Connected, return SftpSessionPtr()); - return SftpSessionPtr(new SftpSession(d->connectionArgs(SshSettings::sftpFilePath()))); -} - void SshConnection::doConnectToHost() { if (d->state != Connecting) diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index d63b5b321f1..40c6b7abf8f 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -120,7 +120,6 @@ public: FileTransferErrorHandling errorHandlingMode); SftpTransferPtr createDownload(const FilesToTransfer &files, FileTransferErrorHandling errorHandlingMode); - SftpSessionPtr createSftpSession(); signals: void connected(); diff --git a/tests/auto/ssh/tst_ssh.cpp b/tests/auto/ssh/tst_ssh.cpp index dbd014fda0e..e579876feb2 100644 --- a/tests/auto/ssh/tst_ssh.cpp +++ b/tests/auto/ssh/tst_ssh.cpp @@ -23,7 +23,6 @@ ** ****************************************************************************/ -#include #include #include #include @@ -150,7 +149,6 @@ void tst_Ssh::pristineConnectionObject() QTest::ignoreMessage(QtDebugMsg, assertToIgnore); QVERIFY(!connection.createRemoteProcess("")); QTest::ignoreMessage(QtDebugMsg, assertToIgnore); - QVERIFY(!connection.createSftpSession()); } void tst_Ssh::remoteProcess_data() @@ -332,10 +330,6 @@ void tst_Ssh::sftp() static const auto getRemoteFilePath = [](const QString &localFileName) { return QString("/tmp/").append(localFileName).append(".upload"); }; - const auto getDownloadFilePath = [](const QTemporaryDir &dirForFilesToDownload, - const QString &localFileName) { - return QString(dirForFilesToDownload.path()).append('/').append(localFileName); - }; FilesToTransfer filesToUpload; std::srand(QDateTime::currentDateTime().toSecsSinceEpoch()); for (int i = 0; i < 100; ++i) { @@ -384,193 +378,6 @@ void tst_Ssh::sftp() QVERIFY(timer.isActive()); timer.stop(); QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - - // Establish interactive SFTP session - SftpSessionPtr sftpChannel = connection.createSftpSession(); - QList 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 - = QDir(dirForFilesToUpload.path()).entryList(QDir::Files); - QCOMPARE(allUploadedFileNames.size(), 101); - for (const QString &fileName : allUploadedFileNames) { - const QString localFilePath = dirForFilesToUpload.path() + '/' + fileName; - const QString remoteFilePath = getRemoteFilePath(fileName); - const QString downloadFilePath = getDownloadFilePath(dirForFilesToDownload, fileName); - const SftpJobId downloadJob = sftpChannel->downloadFile(remoteFilePath, downloadFilePath); - QVERIFY(downloadJob != SftpInvalidJob); - jobs << downloadJob; - } - QCOMPARE(jobs.size(), 101); - loop.exec(); - QVERIFY(!invalidFinishedSignal); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - QCOMPARE(sftpChannel->state(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - - // Compare contents of uploaded and downloaded files - bool success; - const auto compareFiles = [&](const QTemporaryDir &downloadDir) { - success = false; - for (const QString &fileName : allUploadedFileNames) { - QFile originalFile(dirForFilesToUpload.path() + '/' + fileName); - QVERIFY2(originalFile.open(QIODevice::ReadOnly), qPrintable(originalFile.errorString())); - QFile downloadedFile(getDownloadFilePath(downloadDir, fileName)); - QVERIFY2(downloadedFile.open(QIODevice::ReadOnly), - qPrintable(downloadedFile.errorString())); - QVERIFY(originalFile.fileName() != downloadedFile.fileName()); - QCOMPARE(originalFile.size(), downloadedFile.size()); - qint64 bytesLeft = originalFile.size(); - while (bytesLeft > 0) { - const qint64 bytesToRead = qMin(bytesLeft, Q_INT64_C(1024 * 1024)); - const QByteArray origBlock = originalFile.read(bytesToRead); - const QByteArray copyBlock = downloadedFile.read(bytesToRead); - QCOMPARE(origBlock.size(), bytesToRead); - QCOMPARE(origBlock, copyBlock); - bytesLeft -= bytesToRead; - } - } - success = true; - }; - compareFiles(dirForFilesToDownload); - QVERIFY(success); - - // The same again, with a non-interactive download. - const FilesToTransfer filesToDownload = transform(filesToUpload, [&](const FileToTransfer &fileToUpload) { - return FileToTransfer(fileToUpload.targetFile, - getDownloadFilePath(dir2ForFilesToDownload, - QFileInfo(fileToUpload.sourceFile).fileName())); - }); - const SftpTransferPtr download = connection.createDownload(filesToDownload, - FileTransferErrorHandling::Abort); - connect(download.get(), &SftpTransfer::done, [&jobError, &loop](const QString &error) { - jobError = error; - loop.quit(); - }); - QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - timer.setSingleShot(true); - timer.setInterval(30 * 1000); - timer.start(); - download->start(); - loop.exec(); - QVERIFY(timer.isActive()); - timer.stop(); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - compareFiles(dir2ForFilesToDownload); - QVERIFY(success); - - // Remove the uploaded files on the remote system - timer.setInterval((params.timeout + 5) * 1000); - for (const QString &fileName : allUploadedFileNames) { - const QString remoteFilePath = getRemoteFilePath(fileName); - const SftpJobId removeJob = sftpChannel->removeFile(remoteFilePath); - QVERIFY(removeJob != SftpInvalidJob); - jobs << removeJob; - } - loop.exec(); - QVERIFY(!invalidFinishedSignal); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - QCOMPARE(sftpChannel->state(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - - // Create a directory on the remote system - const QString remoteDirPath = "/tmp/sftptest-" + QDateTime::currentDateTime().toString(); - const SftpJobId mkdirJob = sftpChannel->createDirectory(remoteDirPath); - QVERIFY(mkdirJob != SftpInvalidJob); - jobs << mkdirJob; - loop.exec(); - QVERIFY(!invalidFinishedSignal); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - QCOMPARE(sftpChannel->state(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - - // Retrieve and check the attributes of the remote directory - QList remoteFileInfo; - const auto fileInfoHandler - = [&remoteFileInfo](SftpJobId, const QList &fileInfoList) { - remoteFileInfo << fileInfoList; - }; - 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(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - 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 - 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(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - QCOMPARE(remoteFileInfo.size(), 0); - - // Remove the remote directory. - const SftpJobId rmDirJob = sftpChannel->removeDirectory(remoteDirPath); - QVERIFY(rmDirJob != SftpInvalidJob); - jobs << rmDirJob; - loop.exec(); - QVERIFY(!invalidFinishedSignal); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - QCOMPARE(sftpChannel->state(), SftpSession::State::Running); - QVERIFY(jobs.empty()); - - // Closing down - sftpChannel->quit(); - QCOMPARE(sftpChannel->state(), SftpSession::State::Closing); - loop.exec(); - QVERIFY(!invalidFinishedSignal); - QVERIFY2(jobError.isEmpty(), qPrintable(jobError)); - QCOMPARE(sftpChannel->state(), SftpSession::State::Inactive); - connect(&connection, &SshConnection::disconnected, &loop, &QEventLoop::quit); - timer.start(); - connection.disconnectFromHost(); - loop.exec(); - QVERIFY(timer.isActive()); - QCOMPARE(connection.state(), SshConnection::Unconnected); - QVERIFY2(connection.errorString().isEmpty(), qPrintable(connection.errorString())); } void tst_Ssh::cleanupTestCase()