From 5dde520a436dd410269ab84af6814bff12d3c01e Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 7 Oct 2022 16:54:27 +0200 Subject: Utils: Return a bit more data from DeviceShell::runInShell Pass on stderr data and exit code to the caller, it's typically in a better condition to handle errors. Use it to notify the user about non-available 'find' arguments and fix the fallback to ls-based operation. Change-Id: I535535de2ffa09cad1dd6e9b07eb69f807dbae2f Reviewed-by: Marcus Tillmanns Reviewed-by: hjk --- src/libs/utils/deviceshell.cpp | 34 ++----- src/libs/utils/deviceshell.h | 12 +-- src/libs/utils/fileutils.cpp | 31 +++---- src/libs/utils/fileutils.h | 9 +- src/plugins/docker/dockerdevice.cpp | 75 ++++++++-------- src/plugins/remotelinux/linuxdevice.cpp | 110 +++++++++-------------- tests/auto/utils/deviceshell/tst_deviceshell.cpp | 22 +++-- tests/manual/deviceshell/tst_deviceshell.cpp | 4 +- 8 files changed, 125 insertions(+), 172 deletions(-) diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 8daf147ea5..5e43d02021 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -174,31 +174,11 @@ DeviceShell::~DeviceShell() * \param stdInData Data to send to the stdin of the command * \return true if the command finished with EXIT_SUCCESS(0) * - * Runs the cmd inside the internal shell process and return whether it exited with EXIT_SUCCESS + * Runs the cmd inside the internal shell process and return stdout, stderr and exit code * * Will automatically defer to the internal thread */ -bool DeviceShell::runInShell(const CommandLine &cmd, const QByteArray &stdInData) -{ - QTC_ASSERT(m_shellProcess, return false); - Q_ASSERT(QThread::currentThread() != &m_thread); - - const RunResult result = run(cmd, stdInData); - return result.exitCode == 0; -} - -/*! - * \brief DeviceShell::outputForRunInShell - * \param cmd The command to run - * \param stdInData Data to send to the stdin of the command - * \return The stdout of the command - * - * Runs a command inside the running shell and returns the stdout that was generated by it. - * - * Will automatically defer to the internal thread - */ -DeviceShell::RunResult DeviceShell::outputForRunInShell(const CommandLine &cmd, - const QByteArray &stdInData) +RunResult DeviceShell::runInShell(const CommandLine &cmd, const QByteArray &stdInData) { QTC_ASSERT(m_shellProcess, return {}); Q_ASSERT(QThread::currentThread() != &m_thread); @@ -210,7 +190,7 @@ DeviceShell::State DeviceShell::state() const { return m_shellScriptState; } QStringList DeviceShell::missingFeatures() const { return m_missingFeatures; } -DeviceShell::RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) +RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) { if (m_shellScriptState == State::NoScript) { // Fallback ... @@ -539,16 +519,16 @@ void DeviceShell::onReadyRead() QTC_ASSERT(itCmd != m_commandOutput.end(), continue); switch (type) { - case Utils::DeviceShell::ParseType::StdOut: + case ParseType::StdOut: itCmd->stdOut.append(data); break; - case Utils::DeviceShell::ParseType::StdErr: + case ParseType::StdErr: itCmd->stdErr.append(data); break; - case Utils::DeviceShell::ParseType::ExitCode: { + case ParseType::ExitCode: { bool ok = false; int exitCode; - exitCode = QString::fromUtf8(data.begin(), data.size()).toInt(&ok); + exitCode = data.toInt(&ok); QTC_ASSERT(ok, exitCode = -1); itCmd->exitCode = exitCode; itCmd->waiter->wakeOne(); diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index e9382bc885..dcbd3ca905 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -5,6 +5,8 @@ #include "utils_global.h" +#include "fileutils.h" + #include #include #include @@ -28,13 +30,6 @@ class QTCREATOR_UTILS_EXPORT DeviceShell : public QObject public: enum class State { FailedToStart = -1, Unknown = 0, Succeeded = 1, NoScript = 2 }; - struct RunResult - { - int exitCode = 0; - QByteArray stdOut; - QByteArray stdErr; - }; - enum class ParseType { StdOut, StdErr, @@ -46,8 +41,7 @@ public: bool start(); - bool runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - RunResult outputForRunInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); + RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); State state() const; diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 2e147fa43d..ae83517440 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -592,8 +592,6 @@ void FileUtils::iterateLsOutput(const FilePath &base, const FileFilter &filter, const std::function &callBack) { - QTC_CHECK(filter.iteratorFlags != QDirIterator::NoIteratorFlags); // FIXME: Not supported yet below. - const QList nameRegexps = transform(filter.nameFilters, [](const QString &filter) { QRegularExpression re; @@ -625,25 +623,27 @@ void FileUtils::iterateLsOutput(const FilePath &base, // returns whether 'find' could be used. static bool iterateWithFind(const FilePath &filePath, const FileFilter &filter, - const std::function &runInShell, - const std::function &callBack) + const std::function &runInShell, + const std::function &callBack) { QTC_CHECK(filePath.isAbsolutePath()); QStringList arguments{filePath.path()}; arguments << filter.asFindArguments(); - const QByteArray output = runInShell({"find", arguments}); - const QString out = QString::fromUtf8(output.data(), output.size()); - if (!output.isEmpty() && !out.startsWith(filePath.path())) // missing find, unknown option + const RunResult result = runInShell({"find", arguments}); + if (!result.stdErr.isEmpty()) { + // missing find, unknown option e.g. "find: unknown predicate `-L'\n" + // qDebug() << "find error: " << result.stdErr; return false; + } + const QString out = QString::fromUtf8(result.stdOut); const QStringList entries = out.split("\n", Qt::SkipEmptyParts); for (const QString &entry : entries) { - if (!entry.startsWith("find: ")) { - const FilePath fp = FilePath::fromString(entry); - if (!callBack(fp.onDevice(filePath))) - break; - } + const FilePath fp = FilePath::fromString(entry); + // Call back returning 'false' indicates a request to abort iteration. + if (!callBack(fp.onDevice(filePath))) + break; } return true; } @@ -651,7 +651,7 @@ static bool iterateWithFind(const FilePath &filePath, void FileUtils::iterateUnixDirectory(const FilePath &filePath, const FileFilter &filter, bool *useFind, - const std::function &runInShell, + const std::function &runInShell, const std::function &callBack) { QTC_ASSERT(callBack, return); @@ -665,8 +665,9 @@ void FileUtils::iterateUnixDirectory(const FilePath &filePath, } // if we do not have find - use ls as fallback - const QByteArray output = runInShell({"ls", {"-1", "-b", "--", filePath.path()}}); - const QStringList entries = QString::fromUtf8(output).split('\n', Qt::SkipEmptyParts); + // FIXME: Recursion into subdirectories not implemented! + const RunResult result = runInShell({"ls", {"-1", "-b", "--", filePath.path()}}); + const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); FileUtils::iterateLsOutput(filePath, entries, filter, callBack); } diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 09ba6ece63..5a1c5e1f07 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -33,6 +33,13 @@ namespace Utils { class CommandLine; +struct QTCREATOR_UTILS_EXPORT RunResult +{ + int exitCode = 0; + QByteArray stdOut; + QByteArray stdErr; +}; + class QTCREATOR_UTILS_EXPORT FileUtils { public: @@ -89,7 +96,7 @@ public: const FilePath &base, const FileFilter &filter, bool *useFind, - const std::function &runInShell, + const std::function &runInShell, const std::function &callBack); static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 0f08650618..8ab4bb9f8e 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -125,8 +125,10 @@ public: ~DockerDevicePrivate() { stopCurrentContainer(); } - bool runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - QByteArray outputForRunInShell(const CommandLine &cmd); + RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); + bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) { + return runInShell(cmd, stdInData).exitCode == 0; + } std::optional fileContents(const FilePath &filePath, qint64 limit, qint64 offset); @@ -727,76 +729,76 @@ bool DockerDevice::isExecutableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-x", path}}); + return d->runInShellSuccess({"test", {"-x", path}}); } bool DockerDevice::isReadableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-r", path, "-a", "-f", path}}); + return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); } bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-w", path, "-a", "-f", path}}); + return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); } bool DockerDevice::isReadableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-r", path, "-a", "-d", path}}); + return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); } bool DockerDevice::isWritableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-w", path, "-a", "-d", path}}); + return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); } bool DockerDevice::isFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-f", path}}); + return d->runInShellSuccess({"test", {"-f", path}}); } bool DockerDevice::isDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-d", path}}); + return d->runInShellSuccess({"test", {"-d", path}}); } bool DockerDevice::createDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"mkdir", {"-p", path}}); + return d->runInShellSuccess({"mkdir", {"-p", path}}); } bool DockerDevice::exists(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-e", path}}); + return d->runInShellSuccess({"test", {"-e", path}}); } bool DockerDevice::ensureExistingFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"touch", {path}}); + return d->runInShellSuccess({"touch", {path}}); } bool DockerDevice::removeFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShell({"rm", {filePath.path()}}); + return d->runInShellSuccess({"rm", {filePath.path()}}); } bool DockerDevice::removeRecursively(const FilePath &filePath) const @@ -811,28 +813,28 @@ bool DockerDevice::removeRecursively(const FilePath &filePath) const const int levelsNeeded = path.startsWith("/home/") ? 4 : 3; QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - return d->runInShell({"rm", {"-rf", "--", path}}); + return d->runInShellSuccess({"rm", {"-rf", "--", path}}); } bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const { QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); - return d->runInShell({"cp", {filePath.path(), target.path()}}); + return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); } bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const { QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); - return d->runInShell({"mv", {filePath.path(), target.path()}}); + return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); } QDateTime DockerDevice::lastModified(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); - qint64 secs = output.toLongLong(); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); + qint64 secs = result.stdOut.toLongLong(); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); return dt; } @@ -840,24 +842,24 @@ QDateTime DockerDevice::lastModified(const FilePath &filePath) const FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}}); - const QString out = QString::fromUtf8(output.data(), output.size()); + const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); + const QString out = QString::fromUtf8(result.stdOut); return out.isEmpty() ? FilePath() : filePath.withNewPath(out); } qint64 DockerDevice::fileSize(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return -1); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); - return output.toLongLong(); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); + return result.stdOut.toLongLong(); } QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); - const uint bits = output.toUInt(nullptr, 8); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); + const uint bits = result.stdOut.toUInt(nullptr, 8); QFileDevice::Permissions perm = {}; #define BIT(n, p) if (bits & (1<outputForRunInShell(cmd); }; + auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); } @@ -911,7 +913,7 @@ std::optional DockerDevice::fileContents(const FilePath &filePath, bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const { QTC_ASSERT(handlesFile(filePath), return {}); - return d->runInShell({"dd", {"of=" + filePath.path()}}, data); + return d->runInShellSuccess({"dd", {"of=" + filePath.path()}}, data); } Environment DockerDevice::systemEnvironment() const @@ -926,8 +928,8 @@ void DockerDevice::aboutToBeRemoved() const } std::optional DockerDevicePrivate::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) + qint64 limit, + qint64 offset) { updateContainerAccess(); @@ -939,7 +941,7 @@ std::optional DockerDevicePrivate::fileContents(const FilePath &file QString("seek=%1").arg(offset / gcd)}; } - const ContainerShell::RunResult r = m_shell->outputForRunInShell({"dd", args}); + const RunResult r = m_shell->runInShell({"dd", args}); if (r.exitCode != 0) return {}; @@ -952,8 +954,8 @@ void DockerDevicePrivate::fetchSystemEnviroment() updateContainerAccess(); if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) { - const QByteArray output = outputForRunInShell({"env", {}}); - const QString out = QString::fromUtf8(output.data(), output.size()); + const RunResult result = runInShell({"env", {}}); + const QString out = QString::fromUtf8(result.stdOut); m_cachedEnviroment = Environment(out.split('\n', Qt::SkipEmptyParts), q->osType()); return; } @@ -972,20 +974,13 @@ void DockerDevicePrivate::fetchSystemEnviroment() qCWarning(dockerDeviceLog) << "Cannot read container environment:", qPrintable(remoteError); } -bool DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData) +RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData) { updateContainerAccess(); - QTC_ASSERT(m_shell, return false); + QTC_ASSERT(m_shell, return {}); return m_shell->runInShell(cmd, stdInData); } -QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) -{ - updateContainerAccess(); - QTC_ASSERT(m_shell.get(), return {}); - return m_shell->outputForRunInShell(cmd).stdOut; -} - // Factory class DockerImageItem final : public TreeItem, public DockerDeviceData diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 9b5d9c78f7..cf7fe54810 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -357,8 +357,11 @@ public: ~LinuxDevicePrivate(); bool setupShell(); - bool runInShell(const CommandLine &cmd, const QByteArray &data = {}); - std::optional outputForRunInShell(const CommandLine &cmd); + RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); + bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) { + return runInShell(cmd, stdInData).exitCode == 0; + } + void attachToSharedConnection(SshConnectionHandle *connectionHandle, const SshParameters &sshParameters); @@ -462,7 +465,7 @@ qint64 SshProcessInterface::processId() const bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data) { - return d->m_devicePrivate->runInShell(command, data); + return d->m_devicePrivate->runInShellSuccess(command, data); } void SshProcessInterface::start() @@ -807,20 +810,10 @@ public: } // Call me with shell mutex locked - bool runInShell(const CommandLine &cmd, const QByteArray &data = {}) - { - QTC_ASSERT(m_shell, return false); - return m_shell->runInShell(cmd, data); - } - - // Call me with shell mutex locked - std::optional outputForRunInShell(const CommandLine &cmd) + RunResult runInShell(const CommandLine &cmd, const QByteArray &data = {}) { QTC_ASSERT(m_shell, return {}); - const DeviceShell::RunResult result = m_shell->outputForRunInShell(cmd); - if (result.exitCode == 0) - return result.stdOut; - return {}; + return m_shell->runInShell(cmd, data); } void setSshParameters(const SshParameters &sshParameters) @@ -1104,22 +1097,13 @@ bool LinuxDevicePrivate::setupShell() return ok; } -bool LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &data) +RunResult LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &data) { QMutexLocker locker(&m_shellMutex); DEBUG(cmd.toUserOutput()); - QTC_ASSERT(setupShell(), return false); - - return m_handler->runInShell(cmd, data); -} - -std::optional LinuxDevicePrivate::outputForRunInShell(const CommandLine &cmd) -{ - QMutexLocker locker(&m_shellMutex); - DEBUG(cmd); QTC_ASSERT(setupShell(), return {}); - return m_handler->outputForRunInShell(cmd); + return m_handler->runInShell(cmd, data); } void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectionHandle, @@ -1141,56 +1125,56 @@ bool LinuxDevice::isExecutableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-x", path}}); + return d->runInShellSuccess({"test", {"-x", path}}); } bool LinuxDevice::isReadableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-r", path, "-a", "-f", path}}); + return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); } bool LinuxDevice::isWritableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-w", path, "-a", "-f", path}}); + return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); } bool LinuxDevice::isReadableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-r", path, "-a", "-d", path}}); + return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); } bool LinuxDevice::isWritableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-w", path, "-a", "-d", path}}); + return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); } bool LinuxDevice::isFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-f", path}}); + return d->runInShellSuccess({"test", {"-f", path}}); } bool LinuxDevice::isDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-d", path}}); + return d->runInShellSuccess({"test", {"-d", path}}); } bool LinuxDevice::createDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"mkdir", {"-p", path}}); + return d->runInShellSuccess({"mkdir", {"-p", path}}); } bool LinuxDevice::exists(const FilePath &filePath) const @@ -1198,20 +1182,20 @@ bool LinuxDevice::exists(const FilePath &filePath) const DEBUG("filepath " << filePath.path()); QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"test", {"-e", path}}); + return d->runInShellSuccess({"test", {"-e", path}}); } bool LinuxDevice::ensureExistingFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); const QString path = filePath.path(); - return d->runInShell({"touch", {path}}); + return d->runInShellSuccess({"touch", {path}}); } bool LinuxDevice::removeFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShell({"rm", {filePath.path()}}); + return d->runInShellSuccess({"rm", {filePath.path()}}); } bool LinuxDevice::removeRecursively(const FilePath &filePath) const @@ -1226,29 +1210,28 @@ bool LinuxDevice::removeRecursively(const FilePath &filePath) const const int levelsNeeded = path.startsWith("/home/") ? 3 : 2; QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - return d->runInShell({"rm", {"-rf", "--", path}}); + return d->runInShellSuccess({"rm", {"-rf", "--", path}}); } bool LinuxDevice::copyFile(const FilePath &filePath, const FilePath &target) const { QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); - return d->runInShell({"cp", {filePath.path(), target.path()}}); + return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); } bool LinuxDevice::renameFile(const FilePath &filePath, const FilePath &target) const { QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); - return d->runInShell({"mv", {filePath.path(), target.path()}}); + return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); } QDateTime LinuxDevice::lastModified(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}) - .value_or(QByteArray()); - const qint64 secs = output.toLongLong(); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); + const qint64 secs = result.stdOut.toLongLong(); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); return dt; } @@ -1256,18 +1239,16 @@ QDateTime LinuxDevice::lastModified(const FilePath &filePath) const FilePath LinuxDevice::symLinkTarget(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}}) - .value_or(QByteArray()); - const QString out = QString::fromUtf8(output.data(), output.size()); - return output.isEmpty() ? FilePath() : filePath.withNewPath(out); + const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); + return result.stdOut.isEmpty() ? FilePath() + : filePath.withNewPath(QString::fromUtf8(result.stdOut)); } qint64 LinuxDevice::fileSize(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return -1); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%s", filePath.path()}}) - .value_or(QByteArray()); - return output.toLongLong(); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); + return result.stdOut.toLongLong(); } qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const @@ -1275,17 +1256,15 @@ qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const QTC_ASSERT(handlesFile(filePath), return -1); CommandLine cmd("df", {"-k"}); cmd.addArg(filePath.path()); - const QByteArray output = d->outputForRunInShell(cmd).value_or(QByteArray()); - - return FileUtils::bytesAvailableFromDFOutput(output); + const RunResult result = d->runInShell(cmd); + return FileUtils::bytesAvailableFromDFOutput(result.stdOut); } QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); - const QByteArray output = d->outputForRunInShell({"stat", {"-L", "-c", "%a", filePath.path()}}) - .value_or(QByteArray()); - const uint bits = output.toUInt(nullptr, 8); + const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); + const uint bits = result.stdOut.toUInt(nullptr, 8); QFileDevice::Permissions perm = {}; #define BIT(n, p) if (bits & (1<runInShell({"chmod", {QString::number(flags, 16), filePath.path()}}); + return d->runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}}); } void LinuxDevice::iterateDirectory(const FilePath &filePath, @@ -1313,9 +1292,7 @@ void LinuxDevice::iterateDirectory(const FilePath &filePath, const FileFilter &filter) const { QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { - return d->outputForRunInShell(cmd).value_or(QByteArray()); - }; + auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); } @@ -1331,19 +1308,20 @@ std::optional LinuxDevice::fileContents(const FilePath &filePath, } CommandLine cmd(FilePath::fromString("dd"), args, CommandLine::Raw); - const std::optional output = d->outputForRunInShell(cmd); - if (output) { - DEBUG(*output << QByteArray::fromHex(*output)); - } else { + const RunResult result = d->runInShell(cmd); + if (result.exitCode != 0) { DEBUG("fileContents failed"); + return {}; } - return output; + + DEBUG(result.stdOut << QByteArray::fromHex(result.stdOut)); + return result.stdOut; } bool LinuxDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const { QTC_ASSERT(handlesFile(filePath), return {}); - return d->runInShell({"dd", {"of=" + filePath.path()}}, data); + return d->runInShellSuccess({"dd", {"of=" + filePath.path()}}, data); } static FilePaths dirsToCreate(const FilesToTransfer &files) diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp index db4a18a616..ceba7a90ee 100644 --- a/tests/auto/utils/deviceshell/tst_deviceshell.cpp +++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp @@ -162,7 +162,7 @@ private slots: QRandomGenerator generator; - const DeviceShell::RunResult result = shell.outputForRunInShell({"echo", {testData}}); + const RunResult result = shell.runInShell({"echo", {testData}}); QCOMPARE(result.exitCode, 0); const QString expected = testData + "\n"; const QString resultAsUtf8 = QString::fromUtf8(result.stdOut); @@ -210,7 +210,7 @@ private slots: QRandomGenerator generator; - const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, testData.toUtf8()); + const RunResult result = shell.runInShell({"cat", {}}, testData.toUtf8()); QCOMPARE(result.exitCode, 0); const QString resultAsUtf8 = QString::fromUtf8(result.stdOut); QCOMPARE(resultAsUtf8.size(), testData.size()); @@ -236,8 +236,7 @@ private slots: TestShell shell(cmdLine); QCOMPARE(shell.state(), DeviceShell::State::Succeeded); - const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, - m_asciiTestData); + const RunResult result = shell.runInShell({"cat", {}}, m_asciiTestData); QCOMPARE(result.stdOut, m_asciiTestData); } @@ -260,12 +259,11 @@ private slots: TestShell shell(cmdLine); QCOMPARE(shell.state(), DeviceShell::State::Succeeded); - const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, - m_asciiTestData); + const RunResult result = shell.runInShell({"cat", {}}, m_asciiTestData); QCOMPARE(result.stdOut, m_asciiTestData); QVERIFY(result.stdErr.isEmpty()); - const DeviceShell::RunResult result2 = shell.outputForRunInShell( + const RunResult result2 = shell.runInShell( {"cat", {"/tmp/i-do-not-exist.none"}}); QVERIFY(!result2.stdErr.isEmpty()); QVERIFY(result2.exitCode != 0); @@ -290,7 +288,7 @@ private slots: TestShell shell(cmdLine); QCOMPARE(shell.state(), DeviceShell::State::Succeeded); - const DeviceShell::RunResult result = shell.outputForRunInShell({}, {}); + const RunResult result = shell.runInShell({}, {}); QVERIFY(result.exitCode == 255); } @@ -322,7 +320,7 @@ private slots: while (true) { QElapsedTimer t; t.start(); - DeviceShell::RunResult result = shell.outputForRunInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}}); + RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}}); numMs = t.elapsed(); qDebug() << "adjusted maxDepth" << maxDepth << "took" << numMs << "ms"; if (numMs < 100 || maxDepth == 1) { @@ -334,7 +332,7 @@ private slots: QList results = Utils::mapped(runs, [&shell, maxDepth](const int i) -> QByteArray{ QElapsedTimer t; t.start(); - DeviceShell::RunResult result = shell.outputForRunInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}}); + RunResult result = shell.runInShell({"find", {"/usr", "-maxdepth", QString::number(maxDepth)}}); qDebug() << i << "took" << t.elapsed() << "ms"; return result.stdOut; }); @@ -357,8 +355,8 @@ private slots: TestShell shell(cmdLine, true); QCOMPARE(shell.state(), DeviceShell::State::NoScript); - const bool result = shell.runInShell({"echo", {"Hello"}}); - QCOMPARE(result, true); + const RunResult result = shell.runInShell({"echo", {"Hello"}}); + QCOMPARE(result.exitCode, 0); } }; diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp index c65cf0f316..f23efd8231 100644 --- a/tests/manual/deviceshell/tst_deviceshell.cpp +++ b/tests/manual/deviceshell/tst_deviceshell.cpp @@ -100,7 +100,7 @@ class tst_DeviceShell : public QObject const QList result = mapped(testArray, [&shell](QByteArray data) -> QByteArray { - return shell.outputForRunInShell({"cat", {}}, data).stdOut; + return shell.runInShell({"cat", {}}, data).stdOut; }, MapReduceOption::Ordered, QThreadPool::globalInstance()); QCOMPARE(result, testArray); @@ -164,7 +164,7 @@ private slots: TestShell shell; QCOMPARE(shell.state(), DeviceShell::State::Succeeded); - const DeviceShell::RunResult r = shell.outputForRunInShell({"cat", {}}, utf8string.toUtf8()); + const RunResult r = shell.runInShell({"cat", {}}, utf8string.toUtf8()); const QString output = QString::fromUtf8(r.stdOut); QCOMPARE(output, utf8string); } -- cgit v1.2.3