AutoTest: Use QtcProcess for test runner

Change-Id: I29b15a08a260aaf8d2c4071d5ea71d49902d8ef6
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2022-06-10 10:18:34 +02:00
parent dcbeb354f4
commit b28f907114
28 changed files with 79 additions and 67 deletions

View File

@@ -39,7 +39,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *BoostTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto settings = static_cast<BoostTestSettings *>(framework()->testSettings());
return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(),

View File

@@ -36,7 +36,7 @@ public:
explicit BoostTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -29,6 +29,7 @@
#include "boosttesttreeitem.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -41,7 +42,7 @@ namespace Internal {
static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg)
BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile,
LogLevel log, ReportLevel report)
@@ -51,7 +52,7 @@ BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResultPt
, m_reportLevel(report)
{
if (m_testApplication) {
connect(m_testApplication, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
connect(m_testApplication, &Utils::QtcProcess::finished,
this, &BoostTestOutputReader::onFinished);
}
}
@@ -405,7 +406,8 @@ TestResultPtr BoostTestOutputReader::createDefaultResult() const
return TestResultPtr(result);
}
void BoostTestOutputReader::onFinished(int exitCode, QProcess::ExitStatus /*exitState*/) {
void BoostTestOutputReader::onFinished() {
int exitCode = m_testApplication->exitCode();
if (m_reportLevel == ReportLevel::No && m_testCaseCount != -1) {
int reportedFailsAndSkips = m_summary[ResultType::Fail] + m_summary[ResultType::Skip];
m_summary.insert(ResultType::Pass, m_testCaseCount - reportedFailsAndSkips);

View File

@@ -39,7 +39,7 @@ class BoostTestOutputReader : public TestOutputReader
Q_OBJECT
public:
BoostTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, LogLevel log, ReportLevel report);
protected:
void processOutputLine(const QByteArray &outputLine) override;
@@ -47,7 +47,7 @@ protected:
TestResultPtr createDefaultResult() const override;
private:
void onFinished(int exitCode, QProcess::ExitStatus /*exitState*/);
void onFinished();
void sendCompleteInformation();
void handleMessageMatch(const QRegularExpressionMatch &match);
void reportNoOutputFinish(const QString &description, ResultType type);

View File

@@ -35,7 +35,8 @@
namespace Autotest {
namespace Internal {
TestOutputReader *CatchConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi, QProcess *app) const
TestOutputReader *CatchConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
Utils::QtcProcess *app) const
{
return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
}

View File

@@ -34,7 +34,7 @@ class CatchConfiguration : public DebuggableTestConfiguration
public:
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -49,7 +49,8 @@ namespace CatchXml {
}
CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile)
: TestOutputReader (futureInterface, testApplication, buildDirectory)
, m_projectFile(projectFile)

View File

@@ -39,7 +39,7 @@ class CatchOutputReader : public TestOutputReader
public:
CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:

View File

@@ -36,7 +36,7 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase)
}
TestOutputReader *CTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
return new CTestOutputReader(fi, app, workingDirectory());
}

View File

@@ -36,7 +36,7 @@ public:
explicit CTestConfiguration(ITestBase *testBase);
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const final;
Utils::QtcProcess *app) const final;
};
} // namespace Internal

View File

@@ -71,7 +71,7 @@ private:
};
CTestOutputReader::CTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory)
: TestOutputReader(futureInterface, testApplication, buildDirectory)
{

View File

@@ -28,6 +28,8 @@
#include <QCoreApplication>
namespace Utils { class QtcProcess; }
namespace Autotest {
namespace Internal {
@@ -36,7 +38,7 @@ class CTestOutputReader final : public Autotest::TestOutputReader
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::CTestOutputReader)
public:
CTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory);
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
protected:
void processOutputLine(const QByteArray &outputLineWithNewLine) final;

View File

@@ -38,7 +38,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *GTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
return new GTestOutputReader(fi, app, buildDirectory(), projectFile());
}

View File

@@ -37,7 +37,7 @@ public:
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -28,6 +28,7 @@
#include "../testtreemodel.h"
#include "../testtreeitem.h"
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -37,15 +38,16 @@ namespace Autotest {
namespace Internal {
GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile)
: TestOutputReader(futureInterface, testApplication, buildDirectory)
, m_projectFile(projectFile)
{
if (m_testApplication) {
connect(m_testApplication, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this] (int exitCode, QProcess::ExitStatus /*exitStatus*/) {
connect(m_testApplication, &Utils::QtcProcess::finished,
this, [this]() {
int exitCode = m_testApplication->exitCode();
if (exitCode == 1 && !m_description.isEmpty()) {
createAndReportResult(tr("Running tests failed.\n %1\nExecutable: %2")
.arg(m_description).arg(id()), ResultType::MessageFatal);

View File

@@ -38,7 +38,7 @@ class GTestOutputReader : public TestOutputReader
public:
GTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
void processOutputLine(const QByteArray &outputLine) override;

View File

@@ -38,7 +38,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()

View File

@@ -36,7 +36,7 @@ public:
explicit QtTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -126,7 +126,7 @@ static QString constructBenchmarkInformation(const QString &metric, double value
}
QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile,
OutputMode mode, TestType type)

View File

@@ -48,7 +48,7 @@ public:
};
QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, OutputMode mode, TestType type);
protected:
void processOutputLine(const QByteArray &outputLine) override;

View File

@@ -44,7 +44,7 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework)
}
TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()

View File

@@ -35,7 +35,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration
public:
explicit QuickTestConfiguration(ITestFramework *framework);
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -35,9 +35,7 @@
#include <QPointer>
#include <QStringList>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
namespace Utils { class QtcProcess; }
namespace Autotest {
namespace Internal {
@@ -66,7 +64,7 @@ public:
Utils::FilePath executableFilePath() const;
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const = 0;
Utils::QtcProcess *app) const = 0;
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const;
ITestBase *testBase() const { return m_testBase; }

View File

@@ -30,6 +30,7 @@
#include "testtreeitem.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -46,11 +47,12 @@ Utils::FilePath TestOutputReader::constructSourceFilePath(const Utils::FilePath
}
TestOutputReader::TestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory)
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory)
: m_futureInterface(futureInterface)
, m_testApplication(testApplication)
, m_buildDir(buildDirectory)
, m_id(testApplication ? testApplication->program() : QString())
, m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString())
{
auto chopLineBreak = [](QByteArray line) {
if (line.endsWith('\n'))
@@ -61,17 +63,11 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResultPtr> &future
};
if (m_testApplication) {
connect(m_testApplication, &QProcess::readyReadStandardOutput,
this, [chopLineBreak, this] () {
m_testApplication->setReadChannel(QProcess::StandardOutput);
while (m_testApplication->canReadLine())
processStdOutput(chopLineBreak(m_testApplication->readLine()));
m_testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) {
processStdOutput(chopLineBreak(line.toUtf8()));
});
connect(m_testApplication, &QProcess::readyReadStandardError,
this, [chopLineBreak, this] () {
m_testApplication->setReadChannel(QProcess::StandardError);
while (m_testApplication->canReadLine())
processStdError(chopLineBreak(m_testApplication->readLine()));
m_testApplication->setStdErrLineCallback([this, &chopLineBreak](const QString &line) {
processStdError(chopLineBreak(line.toUtf8()));
});
}
}

View File

@@ -29,9 +29,10 @@
#include <QFutureInterface>
#include <QObject>
#include <QProcess>
#include <QString>
namespace Utils { class QtcProcess; }
namespace Autotest {
class TestOutputReader : public QObject
@@ -39,7 +40,7 @@ class TestOutputReader : public QObject
Q_OBJECT
public:
TestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory);
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
virtual ~TestOutputReader();
void processStdOutput(const QByteArray &outputLine);
virtual void processStdError(const QByteArray &outputLine);
@@ -67,7 +68,7 @@ protected:
void reportResult(const TestResultPtr &result);
QFutureInterface<TestResultPtr> m_futureInterface;
QProcess *m_testApplication; // not owned
Utils::QtcProcess *m_testApplication; // not owned
Utils::FilePath m_buildDir;
QString m_id;
QHash<ResultType, int> m_summary;

View File

@@ -66,7 +66,7 @@ public:
ProjectExplorer::Runnable r;
QTC_ASSERT(m_testConfig, return r);
r.command.setExecutable(m_testConfig->executableFilePath());
r.command.setArguments(m_testConfig->argumentsForTestRunner().join(' '));
r.command.addArgs(m_testConfig->argumentsForTestRunner().join(' '), Utils::CommandLine::Raw);
r.workingDirectory = m_testConfig->workingDirectory();
r.environment = m_testConfig->environment();
return r;

View File

@@ -90,6 +90,8 @@ TestRunner::TestRunner()
{
s_instance = this;
m_cancelTimer.setSingleShot(true);
connect(&m_cancelTimer, &QTimer::timeout, this, [this]() { cancelCurrent(Timeout); });
connect(&m_futureWatcher, &QFutureWatcher<TestResultPtr>::resultReadyAt,
this, [this](int index) { emit testResultReady(m_futureWatcher.resultAt(index)); });
connect(&m_futureWatcher, &QFutureWatcher<TestResultPtr>::finished,
@@ -131,16 +133,17 @@ void TestRunner::runTest(TestRunMode mode, const ITestTreeItem *item)
}
}
static QString processInformation(const QProcess *proc)
static QString processInformation(const QtcProcess *proc)
{
QTC_ASSERT(proc, return QString());
QString information("\nCommand line: " + proc->program() + ' ' + proc->arguments().join(' '));
const Utils::CommandLine command = proc->commandLine();
QString information("\nCommand line: " + command.executable().toUserOutput() + ' ' + command.arguments());
QStringList important = { "PATH" };
if (Utils::HostOsInfo::isLinuxHost())
important.append("LD_LIBRARY_PATH");
else if (Utils::HostOsInfo::isMacHost())
important.append({ "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH" });
const QProcessEnvironment &environment = proc->processEnvironment();
const Utils::Environment &environment = proc->environment();
for (const QString &var : important)
information.append('\n' + var + ": " + environment.value(var));
return information;
@@ -204,34 +207,35 @@ bool TestRunner::currentConfigValid()
void TestRunner::setUpProcess()
{
QTC_ASSERT(m_currentConfig, return);
m_currentProcess = new QProcess;
m_currentProcess->setReadChannel(QProcess::StandardOutput);
m_currentProcess = new QtcProcess;
if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
m_currentProcess->setProgram(current->executableFilePath().toString());
m_currentProcess->setCommand({current->executableFilePath(), {}});
} else {
TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
m_currentProcess->setProgram(current->commandLine().executable().toString());
m_currentProcess->setCommand({current->commandLine().executable(), {}});
}
}
void TestRunner::setUpProcessEnv()
{
Utils::CommandLine command = m_currentProcess->commandLine();
if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
QStringList omitted;
m_currentProcess->setArguments(current->argumentsForTestRunner(&omitted));
command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), Utils::CommandLine::Raw);
if (!omitted.isEmpty()) {
const QString &details = constructOmittedDetailsString(omitted);
reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
}
} else {
TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
m_currentProcess->setArguments(current->commandLine().splitArguments());
command.setArguments(current->commandLine().arguments());
}
m_currentProcess->setCommand(command);
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory().toString());
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
const Utils::Environment &original = m_currentConfig->environment();
Utils::Environment environment = m_currentConfig->filteredEnvironment(original);
const Utils::EnvironmentItems removedVariables = Utils::filtered(
@@ -243,7 +247,7 @@ void TestRunner::setUpProcessEnv()
.arg(m_currentConfig->displayName());
reportResult(ResultType::MessageWarn, details);
}
m_currentProcess->setProcessEnvironment(environment.toProcessEnvironment());
m_currentProcess->setEnvironment(environment);
}
void TestRunner::scheduleNext()
@@ -272,15 +276,16 @@ void TestRunner::scheduleNext()
setUpProcessEnv();
connect(m_currentProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
connect(m_currentProcess, &Utils::QtcProcess::finished,
this, &TestRunner::onProcessFinished);
const int timeout = AutotestPlugin::settings()->timeout;
QTimer::singleShot(timeout, m_currentProcess, [this]() { cancelCurrent(Timeout); });
m_cancelTimer.setInterval(timeout);
m_cancelTimer.start();
qCInfo(runnerLog) << "Command:" << m_currentProcess->program();
qCInfo(runnerLog) << "Arguments:" << m_currentProcess->arguments();
qCInfo(runnerLog) << "Command:" << m_currentProcess->commandLine().executable();
qCInfo(runnerLog) << "Arguments:" << m_currentProcess->commandLine().arguments();
qCInfo(runnerLog) << "Working directory:" << m_currentProcess->workingDirectory();
qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment();
qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment().toStringList();
m_currentProcess->start();
if (!m_currentProcess->waitForStarted()) {
@@ -355,7 +360,8 @@ void TestRunner::onProcessFinished()
void TestRunner::resetInternalPointers()
{
delete m_currentOutputReader;
delete m_currentProcess;
if (m_currentProcess)
m_currentProcess->deleteLater();
delete m_currentConfig;
m_currentOutputReader = nullptr;
m_currentProcess = nullptr;
@@ -812,6 +818,7 @@ void TestRunner::onBuildQueueFinished(bool success)
void TestRunner::onFinished()
{
m_cancelTimer.stop();
// if we've been canceled and we still have test configurations queued just throw them away
qDeleteAll(m_selectedTests);
m_selectedTests.clear();

View File

@@ -32,16 +32,17 @@
#include <QFutureWatcher>
#include <QObject>
#include <QQueue>
#include <QTimer>
QT_BEGIN_NAMESPACE
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QProcess;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
namespace Utils { class QtcProcess; }
namespace Autotest {
@@ -105,7 +106,7 @@ private:
bool m_executingTests = false;
bool m_canceled = false;
ITestConfiguration *m_currentConfig = nullptr;
QProcess *m_currentProcess = nullptr;
Utils::QtcProcess *m_currentProcess = nullptr;
TestOutputReader *m_currentOutputReader = nullptr;
TestRunMode m_runMode = TestRunMode::None;
@@ -116,6 +117,7 @@ private:
QMetaObject::Connection m_finishDebugConnect;
// temporarily used for handling of switching the current target
QMetaObject::Connection m_targetConnect;
QTimer m_cancelTimer;
bool m_skipTargetsCheck = false;
};