forked from qt-creator/qt-creator
TestRunner: Reuse TaskTree
Get rid of QFutureInterface argument from ITestConfiguration::createOutputReader() and from TestOutputReader c'tor. The fine-grained progress reporting was broken anyway: 1. The assumption was that testCaseCount was meant to be the total number of test functions executed. It didn't include the initTestCase() and cleanupTestCase(), while those were reported on runtime apparently (and exceeding the max progress by 2). 2. In case of tst_qtcprocess, when the whole test was run, the testCaseCount reported 41, while the real number of functions was 26 (+2 = 28 for init/cleanup). 3. While the max progress was set to testCaseCount initially, the corresponding FutureProgress rendered the progress always in 0-100 range, what didn't match the reality. Instead, rely on TaskTree progress, which resolution is per test as a whole. So, when executing a series of tests this should scale fine. In addition, the progress advances fluently according to the expected run time - with 10 seconds hardcoded. The original code locations, where progress was bumped, are left with a TODO comment for any possible future tweaks. Like in case of result reporting, fine-grained progress reporting may be implemented by providing additional signal, so there is no need for QFutureInterface inside TestOutputReader. Change-Id: Idc11d55e3a49dac8d1788948b9a82f68199203c6 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -11,18 +11,16 @@
|
||||
#include "../testsettings.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestOutputReader *BoostTestConfiguration::createOutputReader(
|
||||
const QFutureInterface<TestResult> &fi, QtcProcess *app) const
|
||||
TestOutputReader *BoostTestConfiguration::createOutputReader(QtcProcess *app) const
|
||||
{
|
||||
auto settings = static_cast<BoostTestSettings *>(framework()->testSettings());
|
||||
return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(),
|
||||
return new BoostTestOutputReader(app, buildDirectory(), projectFile(),
|
||||
LogLevel(settings->logLevel.value()),
|
||||
ReportLevel(settings->reportLevel.value()));
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ class BoostTestConfiguration : public DebuggableTestConfiguration
|
||||
public:
|
||||
explicit BoostTestConfiguration(ITestFramework *framework)
|
||||
: DebuggableTestConfiguration(framework) {}
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const override;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "boosttestsettings.h"
|
||||
#include "boosttestresult.h"
|
||||
|
||||
#include "../autotesttr.h"
|
||||
#include "../testtreeitem.h"
|
||||
|
||||
@@ -21,12 +22,11 @@ namespace Internal {
|
||||
|
||||
static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg)
|
||||
|
||||
BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication,
|
||||
BoostTestOutputReader::BoostTestOutputReader(QtcProcess *testApplication,
|
||||
const FilePath &buildDirectory,
|
||||
const FilePath &projectFile,
|
||||
LogLevel log, ReportLevel report)
|
||||
: TestOutputReader(futureInterface, testApplication, buildDirectory)
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
, m_logLevel(log)
|
||||
, m_reportLevel(report)
|
||||
|
||||
@@ -15,8 +15,7 @@ class BoostTestOutputReader : public TestOutputReader
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
BoostTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
BoostTestOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
const Utils::FilePath &projectFile, LogLevel log, ReportLevel report);
|
||||
protected:
|
||||
void processOutputLine(const QByteArray &outputLine) override;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "catchconfiguration.h"
|
||||
|
||||
#include "catchoutputreader.h"
|
||||
#include "catchtestsettings.h"
|
||||
|
||||
@@ -9,17 +10,14 @@
|
||||
#include "../itestframework.h"
|
||||
#include "../testsettings.h"
|
||||
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestOutputReader *CatchConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
QtcProcess *app) const
|
||||
TestOutputReader *CatchConfiguration::createOutputReader(QtcProcess *app) const
|
||||
{
|
||||
return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
|
||||
return new CatchOutputReader(app, buildDirectory(), projectFile());
|
||||
}
|
||||
|
||||
static QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
|
||||
|
||||
@@ -12,8 +12,7 @@ class CatchConfiguration : public DebuggableTestConfiguration
|
||||
{
|
||||
public:
|
||||
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const override;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "catchoutputreader.h"
|
||||
|
||||
#include "catchresult.h"
|
||||
|
||||
#include "../autotesttr.h"
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
@@ -31,11 +29,10 @@ namespace CatchXml {
|
||||
const char TestCaseResultElement[] = "OverallResult";
|
||||
}
|
||||
|
||||
CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication,
|
||||
CatchOutputReader::CatchOutputReader(QtcProcess *testApplication,
|
||||
const FilePath &buildDirectory,
|
||||
const FilePath &projectFile)
|
||||
: TestOutputReader (futureInterface, testApplication, buildDirectory)
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ namespace Internal {
|
||||
class CatchOutputReader : public TestOutputReader
|
||||
{
|
||||
public:
|
||||
CatchOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
CatchOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
const Utils::FilePath &projectFile);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -13,10 +13,9 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase)
|
||||
setDisplayName("CTest");
|
||||
}
|
||||
|
||||
TestOutputReader *CTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const
|
||||
TestOutputReader *CTestConfiguration::createOutputReader(Utils::QtcProcess *app) const
|
||||
{
|
||||
return new CTestOutputReader(fi, app, workingDirectory());
|
||||
return new CTestOutputReader(app, workingDirectory());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -13,8 +13,7 @@ class CTestConfiguration final : public Autotest::TestToolConfiguration
|
||||
public:
|
||||
explicit CTestConfiguration(ITestBase *testBase);
|
||||
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const final;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const final;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
|
||||
#include "../autotesttr.h"
|
||||
#include "../testframeworkmanager.h"
|
||||
#include "../testresult.h"
|
||||
#include "../testtreeitem.h"
|
||||
|
||||
#include <cmakeprojectmanager/cmakeprojectconstants.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
@@ -52,10 +50,9 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
CTestOutputReader::CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication,
|
||||
CTestOutputReader::CTestOutputReader(QtcProcess *testApplication,
|
||||
const FilePath &buildDirectory)
|
||||
: TestOutputReader(futureInterface, testApplication, buildDirectory)
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ namespace Internal {
|
||||
class CTestOutputReader final : public Autotest::TestOutputReader
|
||||
{
|
||||
public:
|
||||
CTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
|
||||
CTestOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
|
||||
|
||||
protected:
|
||||
void processOutputLine(const QByteArray &outputLineWithNewLine) final;
|
||||
|
||||
@@ -5,22 +5,21 @@
|
||||
|
||||
#include "gtestoutputreader.h"
|
||||
#include "gtestsettings.h"
|
||||
|
||||
#include "../autotestplugin.h"
|
||||
#include "../itestframework.h"
|
||||
#include "../testsettings.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestOutputReader *GTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
QtcProcess *app) const
|
||||
TestOutputReader *GTestConfiguration::createOutputReader(QtcProcess *app) const
|
||||
{
|
||||
return new GTestOutputReader(fi, app, buildDirectory(), projectFile());
|
||||
return new GTestOutputReader(app, buildDirectory(), projectFile());
|
||||
}
|
||||
|
||||
QStringList filterInterfering(const QStringList &provided, QStringList *omitted)
|
||||
|
||||
@@ -14,8 +14,7 @@ public:
|
||||
explicit GTestConfiguration(ITestFramework *framework)
|
||||
: DebuggableTestConfiguration(framework) {}
|
||||
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const override;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
@@ -17,11 +17,10 @@ using namespace Utils;
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication,
|
||||
GTestOutputReader::GTestOutputReader(QtcProcess *testApplication,
|
||||
const FilePath &buildDirectory,
|
||||
const FilePath &projectFile)
|
||||
: TestOutputReader(futureInterface, testApplication, buildDirectory)
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
{
|
||||
if (testApplication) {
|
||||
@@ -121,7 +120,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
testResult.setResult(ResultType::MessageInternal);
|
||||
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
|
||||
reportResult(testResult);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
// TODO: bump progress?
|
||||
} else if (ExactMatch match = testSetFail.match(line)) {
|
||||
m_testSetStarted = false;
|
||||
TestResult testResult = createDefaultResult();
|
||||
@@ -132,7 +131,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
testResult.setResult(ResultType::MessageInternal);
|
||||
testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2)));
|
||||
reportResult(testResult);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
// TODO: bump progress?
|
||||
} else if (ExactMatch match = testSetSkipped.match(line)) {
|
||||
if (!m_testSetStarted) // ignore SKIPPED at summary
|
||||
return;
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace Internal {
|
||||
class GTestOutputReader : public TestOutputReader
|
||||
{
|
||||
public:
|
||||
GTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
GTestOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
const Utils::FilePath &projectFile);
|
||||
protected:
|
||||
void processOutputLine(const QByteArray &outputLine) override;
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "qttestconfiguration.h"
|
||||
#include "qttestconstants.h"
|
||||
|
||||
#include "qttestoutputreader.h"
|
||||
#include "qttestsettings.h"
|
||||
#include "qttest_utils.h"
|
||||
|
||||
#include "../autotestplugin.h"
|
||||
#include "../itestframework.h"
|
||||
#include "../testsettings.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -28,14 +28,13 @@ static QStringList quoteIfNeeded(const QStringList &testCases, bool debugMode)
|
||||
});
|
||||
}
|
||||
|
||||
TestOutputReader *QtTestConfiguration::createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
QtcProcess *app) const
|
||||
TestOutputReader *QtTestConfiguration::createOutputReader(QtcProcess *app) const
|
||||
{
|
||||
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
|
||||
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
|
||||
? QtTestOutputReader::XML
|
||||
: QtTestOutputReader::PlainText;
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(), mode, TestType::QtTest);
|
||||
return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QtTest);
|
||||
}
|
||||
|
||||
QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
|
||||
|
||||
@@ -13,8 +13,7 @@ class QtTestConfiguration : public DebuggableTestConfiguration
|
||||
public:
|
||||
explicit QtTestConfiguration(ITestFramework *framework)
|
||||
: DebuggableTestConfiguration(framework) {}
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const override;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
@@ -99,18 +97,15 @@ static QString constructBenchmarkInformation(const QString &metric, double value
|
||||
else if (metric == "CPUCycles") // -perf
|
||||
metricsText = "CPU cycles";
|
||||
return Tr::tr("%1 %2 per iteration (total: %3, iterations: %4)")
|
||||
.arg(formatResult(value))
|
||||
.arg(metricsText)
|
||||
.arg(formatResult(value * double(iterations)))
|
||||
.arg(formatResult(value), metricsText, formatResult(value * double(iterations)))
|
||||
.arg(iterations);
|
||||
}
|
||||
|
||||
QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication,
|
||||
QtTestOutputReader::QtTestOutputReader(QtcProcess *testApplication,
|
||||
const FilePath &buildDirectory,
|
||||
const FilePath &projectFile,
|
||||
OutputMode mode, TestType type)
|
||||
: TestOutputReader(futureInterface, testApplication, buildDirectory)
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
, m_mode(mode)
|
||||
, m_testType(type)
|
||||
@@ -177,8 +172,6 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
|
||||
m_xmlReader.addData("\n");
|
||||
m_xmlReader.addData(QString::fromUtf8(outputLine));
|
||||
while (!m_xmlReader.atEnd()) {
|
||||
if (m_futureInterface.isCanceled())
|
||||
return;
|
||||
QXmlStreamReader::TokenType token = m_xmlReader.readNext();
|
||||
switch (token) {
|
||||
case QXmlStreamReader::StartDocument:
|
||||
@@ -277,7 +270,7 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
|
||||
const QStringView currentTag = m_xmlReader.name();
|
||||
if (currentTag == QStringLiteral("TestFunction")) {
|
||||
sendFinishMessage(true);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
// TODO: bump progress?
|
||||
m_dataTag.clear();
|
||||
m_formerTestCase = m_testCase;
|
||||
m_testCase.clear();
|
||||
@@ -347,9 +340,6 @@ void QtTestOutputReader::processPlainTextOutput(const QByteArray &outputLine)
|
||||
static const QRegularExpression locationUnix(QT_TEST_FAIL_UNIX_REGEXP);
|
||||
static const QRegularExpression locationWin(QT_TEST_FAIL_WIN_REGEXP);
|
||||
|
||||
if (m_futureInterface.isCanceled())
|
||||
return;
|
||||
|
||||
const QString line = QString::fromUtf8(outputLine);
|
||||
QRegularExpressionMatch match;
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qttestconstants.h"
|
||||
#include "../testoutputreader.h"
|
||||
|
||||
#include "qttestconstants.h"
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
namespace Autotest {
|
||||
@@ -22,8 +23,7 @@ public:
|
||||
PlainText
|
||||
};
|
||||
|
||||
QtTestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
QtTestOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
|
||||
const Utils::FilePath &projectFile, OutputMode mode, TestType type);
|
||||
protected:
|
||||
void processOutputLine(const QByteArray &outputLine) override;
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "quicktestconfiguration.h"
|
||||
#include "../qtest/qttestconstants.h"
|
||||
|
||||
#include "../autotestplugin.h"
|
||||
#include "../itestframework.h"
|
||||
#include "../qtest/qttestoutputreader.h"
|
||||
#include "../qtest/qttestsettings.h"
|
||||
#include "../qtest/qttest_utils.h"
|
||||
#include "../autotestplugin.h"
|
||||
#include "../itestframework.h"
|
||||
#include "../testsettings.h"
|
||||
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
@@ -23,15 +21,13 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework)
|
||||
setMixedDebugging(true);
|
||||
}
|
||||
|
||||
TestOutputReader *QuickTestConfiguration::createOutputReader(
|
||||
const QFutureInterface<TestResult> &fi, QtcProcess *app) const
|
||||
TestOutputReader *QuickTestConfiguration::createOutputReader(QtcProcess *app) const
|
||||
{
|
||||
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
|
||||
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()
|
||||
? QtTestOutputReader::XML
|
||||
: QtTestOutputReader::PlainText;
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), projectFile(),
|
||||
mode, TestType::QuickTest);
|
||||
return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QuickTest);
|
||||
}
|
||||
|
||||
QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const
|
||||
|
||||
@@ -12,8 +12,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration
|
||||
{
|
||||
public:
|
||||
explicit QuickTestConfiguration(ITestFramework *framework);
|
||||
TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const override;
|
||||
TestOutputReader *createOutputReader(Utils::QtcProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
@@ -4,22 +4,19 @@
|
||||
#include "testconfiguration.h"
|
||||
|
||||
#include "itestframework.h"
|
||||
#include "testoutputreader.h"
|
||||
#include "testrunconfiguration.h"
|
||||
|
||||
#include <cppeditor/cppmodelmanager.h>
|
||||
#include <cppeditor/projectinfo.h>
|
||||
|
||||
#include <projectexplorer/buildconfiguration.h>
|
||||
#include <projectexplorer/buildsystem.h>
|
||||
#include <projectexplorer/buildtargetinfo.h>
|
||||
#include <projectexplorer/deploymentdata.h>
|
||||
#include <projectexplorer/environmentaspect.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/runconfiguration.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testconfiguration", QtWarningMsg)
|
||||
@@ -29,7 +26,6 @@ using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
|
||||
|
||||
ITestConfiguration::ITestConfiguration(ITestBase *testBase)
|
||||
: m_testBase(testBase)
|
||||
{
|
||||
@@ -94,7 +90,7 @@ static FilePath ensureExeEnding(const FilePath &file)
|
||||
return file.withExecutableSuffix();
|
||||
}
|
||||
|
||||
void TestConfiguration::completeTestInformation(ProjectExplorer::RunConfiguration *rc,
|
||||
void TestConfiguration::completeTestInformation(RunConfiguration *rc,
|
||||
TestRunMode runMode)
|
||||
{
|
||||
QTC_ASSERT(rc, return);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <projectexplorer/runcontrol.h>
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <QFutureInterface>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
|
||||
@@ -40,8 +39,7 @@ public:
|
||||
Utils::FilePath executableFilePath() const;
|
||||
virtual Utils::FilePath testExecutable() const { return executableFilePath(); };
|
||||
|
||||
virtual TestOutputReader *createOutputReader(const QFutureInterface<TestResult> &fi,
|
||||
Utils::QtcProcess *app) const = 0;
|
||||
virtual TestOutputReader *createOutputReader(Utils::QtcProcess *app) const = 0;
|
||||
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const;
|
||||
|
||||
ITestBase *testBase() const { return m_testBase; }
|
||||
|
||||
@@ -4,17 +4,12 @@
|
||||
#include "testoutputreader.h"
|
||||
|
||||
#include "autotesttr.h"
|
||||
#include "testresult.h"
|
||||
#include "testresultspane.h"
|
||||
#include "testtreeitem.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -26,11 +21,8 @@ FilePath TestOutputReader::constructSourceFilePath(const FilePath &path, const Q
|
||||
return filePath.isReadableFile() ? filePath : FilePath();
|
||||
}
|
||||
|
||||
TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
QtcProcess *testApplication, const FilePath &buildDirectory)
|
||||
: m_futureInterface(futureInterface)
|
||||
, m_buildDir(buildDirectory)
|
||||
, m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString())
|
||||
TestOutputReader::TestOutputReader(QtcProcess *testApplication, const FilePath &buildDirectory)
|
||||
: m_buildDir(buildDirectory)
|
||||
{
|
||||
auto chopLineBreak = [](QByteArray line) {
|
||||
if (line.endsWith('\n'))
|
||||
@@ -41,6 +33,9 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResult> &futureInt
|
||||
};
|
||||
|
||||
if (testApplication) {
|
||||
connect(testApplication, &QtcProcess::started, this, [this, testApplication] {
|
||||
m_id = testApplication->commandLine().executable().toUserOutput();
|
||||
});
|
||||
testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) {
|
||||
processStdOutput(chopLineBreak(line.toUtf8()));
|
||||
});
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
|
||||
#include "testresult.h"
|
||||
|
||||
#include <QFutureInterface>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
namespace Utils { class QtcProcess; }
|
||||
|
||||
@@ -17,8 +15,7 @@ class TestOutputReader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestOutputReader(const QFutureInterface<TestResult> &futureInterface,
|
||||
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
|
||||
TestOutputReader(Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
|
||||
virtual ~TestOutputReader();
|
||||
void processStdOutput(const QByteArray &outputLine);
|
||||
virtual void processStdError(const QByteArray &outputLine);
|
||||
@@ -46,7 +43,6 @@ protected:
|
||||
void sendAndResetSanitizerResult();
|
||||
|
||||
void reportResult(const TestResult &result);
|
||||
QFutureInterface<TestResult> m_futureInterface;
|
||||
Utils::FilePath m_buildDir;
|
||||
QString m_id;
|
||||
QHash<ResultType, int> m_summary;
|
||||
|
||||
@@ -6,18 +6,15 @@
|
||||
#include "autotestconstants.h"
|
||||
#include "autotestplugin.h"
|
||||
#include "autotesttr.h"
|
||||
#include "itestframework.h"
|
||||
#include "testoutputreader.h"
|
||||
#include "testprojectsettings.h"
|
||||
#include "testresultspane.h"
|
||||
#include "testrunconfiguration.h"
|
||||
#include "testsettings.h"
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <coreplugin/progressmanager/taskprogress.h>
|
||||
|
||||
#include <debugger/debuggerkitinformation.h>
|
||||
#include <debugger/debuggerruncontrol.h>
|
||||
@@ -47,10 +44,9 @@
|
||||
#include <QLabel>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointer>
|
||||
#include <QProcess>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
@@ -72,14 +68,7 @@ TestRunner::TestRunner()
|
||||
|
||||
m_cancelTimer.setSingleShot(true);
|
||||
connect(&m_cancelTimer, &QTimer::timeout, this, [this] { cancelCurrent(Timeout); });
|
||||
connect(&m_futureWatcher, &QFutureWatcher<TestResult>::finished,
|
||||
this, &TestRunner::onFinished);
|
||||
connect(this, &TestRunner::requestStopTestRun,
|
||||
&m_futureWatcher, &QFutureWatcher<TestResult>::cancel);
|
||||
connect(&m_futureWatcher, &QFutureWatcher<TestResult>::canceled, this, [this] {
|
||||
cancelCurrent(UserCanceled);
|
||||
reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
|
||||
});
|
||||
connect(this, &TestRunner::requestStopTestRun, this, [this] { cancelCurrent(UserCanceled); });
|
||||
connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
|
||||
this, &TestRunner::onBuildQueueFinished);
|
||||
}
|
||||
@@ -93,7 +82,7 @@ TestRunner::~TestRunner()
|
||||
|
||||
void TestRunner::runTest(TestRunMode mode, const ITestTreeItem *item)
|
||||
{
|
||||
QTC_ASSERT(!m_executingTests, return);
|
||||
QTC_ASSERT(!isTestRunning(), return);
|
||||
ITestConfiguration *configuration = item->asConfiguration(mode);
|
||||
|
||||
if (configuration)
|
||||
@@ -144,180 +133,21 @@ static QString constructOmittedVariablesDetailsString(const EnvironmentItems &di
|
||||
+ '\n' + removedVars.join('\n');
|
||||
}
|
||||
|
||||
bool TestRunner::currentConfigValid()
|
||||
{
|
||||
const FilePath commandFilePath = m_currentConfig->testExecutable();
|
||||
if (!commandFilePath.isEmpty())
|
||||
return true;
|
||||
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Executable path is empty. (%1)").arg(m_currentConfig->displayName()));
|
||||
delete m_currentConfig;
|
||||
m_currentConfig = nullptr;
|
||||
if (m_selectedTests.isEmpty()) {
|
||||
if (m_fakeFutureInterface)
|
||||
m_fakeFutureInterface->reportFinished();
|
||||
onFinished();
|
||||
} else {
|
||||
onProcessDone();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestRunner::setUpProcessEnv()
|
||||
{
|
||||
CommandLine command = m_currentProcess->commandLine();
|
||||
if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
|
||||
TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
|
||||
|
||||
QStringList omitted;
|
||||
command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), 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);
|
||||
command.setArguments(current->commandLine().arguments());
|
||||
}
|
||||
m_currentProcess->setCommand(command);
|
||||
|
||||
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
|
||||
const Environment &original = m_currentConfig->environment();
|
||||
Environment environment = m_currentConfig->filteredEnvironment(original);
|
||||
const EnvironmentItems removedVariables = Utils::filtered(
|
||||
original.diff(environment), [](const EnvironmentItem &it) {
|
||||
return it.operation == EnvironmentItem::Unset;
|
||||
});
|
||||
if (!removedVariables.isEmpty()) {
|
||||
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
|
||||
.arg(m_currentConfig->displayName());
|
||||
reportResult(ResultType::MessageWarn, details);
|
||||
}
|
||||
m_currentProcess->setEnvironment(environment);
|
||||
}
|
||||
|
||||
void TestRunner::scheduleNext()
|
||||
{
|
||||
QTC_ASSERT(!m_selectedTests.isEmpty(), onFinished(); return);
|
||||
QTC_ASSERT(!m_currentConfig && !m_currentProcess, resetInternalPointers());
|
||||
QTC_ASSERT(m_fakeFutureInterface, onFinished(); return);
|
||||
QTC_ASSERT(!m_canceled, onFinished(); return);
|
||||
|
||||
m_currentConfig = m_selectedTests.takeFirst();
|
||||
|
||||
if (!currentConfigValid())
|
||||
return;
|
||||
|
||||
if (!m_currentConfig->project())
|
||||
onProcessDone();
|
||||
|
||||
m_currentProcess = new QtcProcess;
|
||||
m_currentProcess->setCommand({m_currentConfig->testExecutable(), {}});
|
||||
|
||||
QTC_ASSERT(!m_currentOutputReader, delete m_currentOutputReader);
|
||||
m_currentOutputReader = m_currentConfig->createOutputReader(*m_fakeFutureInterface, m_currentProcess);
|
||||
QTC_ASSERT(m_currentOutputReader, onProcessDone(); return);
|
||||
connect(m_currentOutputReader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
|
||||
connect(m_currentOutputReader, &TestOutputReader::newOutputLineAvailable,
|
||||
TestResultsPane::instance(), &TestResultsPane::addOutputLine);
|
||||
|
||||
setUpProcessEnv();
|
||||
|
||||
connect(m_currentProcess, &QtcProcess::done, this, &TestRunner::onProcessDone);
|
||||
const int timeout = AutotestPlugin::settings()->timeout;
|
||||
m_cancelTimer.setInterval(timeout);
|
||||
m_cancelTimer.start();
|
||||
|
||||
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().toStringList();
|
||||
|
||||
m_currentProcess->start();
|
||||
}
|
||||
|
||||
void TestRunner::cancelCurrent(TestRunner::CancelReason reason)
|
||||
{
|
||||
m_canceled = true;
|
||||
|
||||
if (m_fakeFutureInterface)
|
||||
m_fakeFutureInterface->reportCanceled();
|
||||
|
||||
if (reason == KitChanged)
|
||||
reportResult(ResultType::MessageWarn, Tr::tr("Current kit has changed. Canceling test run."));
|
||||
else if (reason == Timeout)
|
||||
reportResult(ResultType::MessageFatal, Tr::tr("Test case canceled due to timeout.\nMaybe raise the timeout?"));
|
||||
|
||||
// if user or timeout cancels the current run ensure to kill the running process
|
||||
if (m_currentProcess && m_currentProcess->state() != QProcess::NotRunning) {
|
||||
m_currentProcess->kill();
|
||||
m_currentProcess->waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void TestRunner::onProcessDone()
|
||||
{
|
||||
if (m_currentProcess->result() == ProcessResult::StartFailed) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Failed to start test for project \"%1\".").arg(m_currentConfig->displayName())
|
||||
+ processInformation(m_currentProcess) + rcInfo(m_currentConfig));
|
||||
}
|
||||
|
||||
if (m_executingTests && m_currentConfig) {
|
||||
QTC_CHECK(m_fakeFutureInterface);
|
||||
m_fakeFutureInterface->setProgressValue(m_fakeFutureInterface->progressValue()
|
||||
+ m_currentConfig->testCaseCount());
|
||||
if (m_currentProcess && !m_fakeFutureInterface->isCanceled()) {
|
||||
if (m_currentProcess->exitStatus() == QProcess::CrashExit) {
|
||||
if (m_currentOutputReader)
|
||||
m_currentOutputReader->reportCrash();
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Test for project \"%1\" crashed.").arg(m_currentConfig->displayName())
|
||||
+ processInformation(m_currentProcess) + rcInfo(m_currentConfig));
|
||||
} else if (m_currentOutputReader && !m_currentOutputReader->hadValidOutput()) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Test for project \"%1\" did not produce any expected output.")
|
||||
.arg(m_currentConfig->displayName()) + processInformation(m_currentProcess)
|
||||
+ rcInfo(m_currentConfig));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_currentOutputReader) {
|
||||
const int disabled = m_currentOutputReader->disabledTests();
|
||||
if (disabled > 0)
|
||||
emit hadDisabledTests(disabled);
|
||||
if (m_currentOutputReader->hasSummary())
|
||||
emit reportSummary(m_currentOutputReader->id(), m_currentOutputReader->summary());
|
||||
|
||||
m_currentOutputReader->resetCommandlineColor();
|
||||
}
|
||||
resetInternalPointers();
|
||||
|
||||
if (!m_fakeFutureInterface) {
|
||||
QTC_ASSERT(!m_executingTests, m_executingTests = false);
|
||||
return;
|
||||
}
|
||||
if (!m_selectedTests.isEmpty() && !m_fakeFutureInterface->isCanceled())
|
||||
scheduleNext();
|
||||
else
|
||||
m_fakeFutureInterface->reportFinished();
|
||||
}
|
||||
|
||||
void TestRunner::resetInternalPointers()
|
||||
{
|
||||
delete m_currentOutputReader;
|
||||
if (m_currentProcess)
|
||||
m_currentProcess->deleteLater();
|
||||
delete m_currentConfig;
|
||||
m_currentOutputReader = nullptr;
|
||||
m_currentProcess = nullptr;
|
||||
m_currentConfig = nullptr;
|
||||
else if (reason == UserCanceled)
|
||||
reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user."));
|
||||
m_taskTree.reset();
|
||||
onFinished();
|
||||
}
|
||||
|
||||
void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests)
|
||||
{
|
||||
QTC_ASSERT(!m_executingTests, return);
|
||||
QTC_ASSERT(!isTestRunning(), return);
|
||||
qDeleteAll(m_selectedTests);
|
||||
m_selectedTests = selectedTests;
|
||||
|
||||
@@ -332,8 +162,6 @@ void TestRunner::runTests(TestRunMode mode, const QList<ITestConfiguration *> &s
|
||||
return;
|
||||
}
|
||||
|
||||
m_executingTests = true;
|
||||
m_canceled = false;
|
||||
emit testRunStarted();
|
||||
|
||||
// clear old log and output pane
|
||||
@@ -411,7 +239,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey)
|
||||
if (runConfigurations.size() == 1)
|
||||
return runConfigurations.first();
|
||||
|
||||
RunConfigurationSelectionDialog dialog(buildTargetKey, Core::ICore::dialogParent());
|
||||
RunConfigurationSelectionDialog dialog(buildTargetKey, ICore::dialogParent());
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
const QString dName = dialog.displayName();
|
||||
if (dName.isEmpty())
|
||||
@@ -513,19 +341,136 @@ void TestRunner::runTestsHelper()
|
||||
return;
|
||||
}
|
||||
|
||||
int testCaseCount = precheckTestConfigurations();
|
||||
const int testCaseCount = precheckTestConfigurations();
|
||||
Q_UNUSED(testCaseCount) // TODO: may be useful for fine-grained progress reporting, when fixed
|
||||
|
||||
// Fake future interface - destruction will be handled by QFuture/QFutureWatcher
|
||||
m_fakeFutureInterface = new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
|
||||
QFuture<TestResult> future = m_fakeFutureInterface->future();
|
||||
m_fakeFutureInterface->setProgressRange(0, testCaseCount);
|
||||
m_fakeFutureInterface->setProgressValue(0);
|
||||
m_futureWatcher.setFuture(future);
|
||||
struct TestStorage {
|
||||
std::unique_ptr<TestOutputReader> m_outputReader;
|
||||
};
|
||||
|
||||
using namespace Tasking;
|
||||
QList<TaskItem> tasks{optional};
|
||||
|
||||
for (ITestConfiguration *config : m_selectedTests) {
|
||||
QTC_ASSERT(config, continue);
|
||||
const TreeStorage<TestStorage> storage;
|
||||
|
||||
const auto onGroupSetup = [this, config] {
|
||||
if (!config->project())
|
||||
return TaskAction::StopWithDone;
|
||||
if (config->testExecutable().isEmpty()) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Executable path is empty. (%1)").arg(config->displayName()));
|
||||
return TaskAction::StopWithDone;
|
||||
}
|
||||
return TaskAction::Continue;
|
||||
};
|
||||
const auto onSetup = [this, config, storage](QtcProcess &process) {
|
||||
TestStorage *testStorage = storage.activeStorage();
|
||||
QTC_ASSERT(testStorage, return);
|
||||
testStorage->m_outputReader.reset(config->createOutputReader(&process));
|
||||
QTC_ASSERT(testStorage->m_outputReader, return);
|
||||
connect(testStorage->m_outputReader.get(), &TestOutputReader::newResult,
|
||||
this, &TestRunner::testResultReady);
|
||||
connect(testStorage->m_outputReader.get(), &TestOutputReader::newOutputLineAvailable,
|
||||
TestResultsPane::instance(), &TestResultsPane::addOutputLine);
|
||||
|
||||
CommandLine command{config->testExecutable(), {}};
|
||||
if (config->testBase()->type() == ITestBase::Framework) {
|
||||
TestConfiguration *current = static_cast<TestConfiguration *>(config);
|
||||
QStringList omitted;
|
||||
command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), CommandLine::Raw);
|
||||
if (!omitted.isEmpty()) {
|
||||
const QString &details = constructOmittedDetailsString(omitted);
|
||||
reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
|
||||
}
|
||||
} else {
|
||||
TestToolConfiguration *current = static_cast<TestToolConfiguration *>(config);
|
||||
command.setArguments(current->commandLine().arguments());
|
||||
}
|
||||
process.setCommand(command);
|
||||
|
||||
process.setWorkingDirectory(config->workingDirectory());
|
||||
const Environment &original = config->environment();
|
||||
Environment environment = config->filteredEnvironment(original);
|
||||
const EnvironmentItems removedVariables = Utils::filtered(
|
||||
original.diff(environment), [](const EnvironmentItem &it) {
|
||||
return it.operation == EnvironmentItem::Unset;
|
||||
});
|
||||
if (!removedVariables.isEmpty()) {
|
||||
const QString &details = constructOmittedVariablesDetailsString(removedVariables)
|
||||
.arg(config->displayName());
|
||||
reportResult(ResultType::MessageWarn, details);
|
||||
}
|
||||
process.setEnvironment(environment);
|
||||
|
||||
m_cancelTimer.setInterval(AutotestPlugin::settings()->timeout);
|
||||
m_cancelTimer.start();
|
||||
|
||||
qCInfo(runnerLog) << "Command:" << process.commandLine().executable();
|
||||
qCInfo(runnerLog) << "Arguments:" << process.commandLine().arguments();
|
||||
qCInfo(runnerLog) << "Working directory:" << process.workingDirectory();
|
||||
qCDebug(runnerLog) << "Environment:" << process.environment().toStringList();
|
||||
};
|
||||
const auto onDone = [this, config, storage](const QtcProcess &process) {
|
||||
TestStorage *testStorage = storage.activeStorage();
|
||||
QTC_ASSERT(testStorage, return);
|
||||
if (process.result() == ProcessResult::StartFailed) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Failed to start test for project \"%1\".").arg(config->displayName())
|
||||
+ processInformation(&process) + rcInfo(config));
|
||||
}
|
||||
|
||||
if (process.exitStatus() == QProcess::CrashExit) {
|
||||
if (testStorage->m_outputReader)
|
||||
testStorage->m_outputReader->reportCrash();
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Test for project \"%1\" crashed.").arg(config->displayName())
|
||||
+ processInformation(&process) + rcInfo(config));
|
||||
} else if (testStorage->m_outputReader && !testStorage->m_outputReader->hadValidOutput()) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Test for project \"%1\" did not produce any expected output.")
|
||||
.arg(config->displayName()) + processInformation(&process)
|
||||
+ rcInfo(config));
|
||||
}
|
||||
if (testStorage->m_outputReader) {
|
||||
const int disabled = testStorage->m_outputReader->disabledTests();
|
||||
if (disabled > 0)
|
||||
emit hadDisabledTests(disabled);
|
||||
if (testStorage->m_outputReader->hasSummary())
|
||||
emit reportSummary(testStorage->m_outputReader->id(), testStorage->m_outputReader->summary());
|
||||
|
||||
testStorage->m_outputReader->resetCommandlineColor();
|
||||
}
|
||||
};
|
||||
const Group group {
|
||||
optional,
|
||||
Storage(storage),
|
||||
OnGroupSetup(onGroupSetup),
|
||||
Process(onSetup, onDone, onDone)
|
||||
};
|
||||
tasks.append(group);
|
||||
}
|
||||
|
||||
m_taskTree.reset(new TaskTree(tasks));
|
||||
connect(m_taskTree.get(), &TaskTree::done, this, &TestRunner::onFinished);
|
||||
connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &TestRunner::onFinished);
|
||||
|
||||
auto progress = new TaskProgress(m_taskTree.get());
|
||||
progress->setDisplayName(tr("Running Tests"));
|
||||
progress->setAutoStopOnCancel(false);
|
||||
progress->setHalfLifeTimePerTask(10000); // 10 seconds
|
||||
connect(progress, &TaskProgress::canceled, this, [this, progress] {
|
||||
// progress was a child of task tree which is going to be deleted directly. Unwind properly.
|
||||
progress->setParent(nullptr);
|
||||
progress->deleteLater();
|
||||
cancelCurrent(UserCanceled);
|
||||
});
|
||||
|
||||
Core::ProgressManager::addTask(future, Tr::tr("Running Tests"), Autotest::Constants::TASK_INDEX);
|
||||
if (AutotestPlugin::settings()->popupOnStart)
|
||||
AutotestPlugin::popupResultsPane();
|
||||
scheduleNext();
|
||||
|
||||
m_taskTree->start();
|
||||
}
|
||||
|
||||
static void processOutput(TestOutputReader *outputreader, const QString &msg, OutputFormat format)
|
||||
@@ -626,13 +571,8 @@ void TestRunner::debugTests()
|
||||
}
|
||||
}
|
||||
|
||||
// We need a fake QFuture for the results. TODO: replace with QtConcurrent::run
|
||||
QFutureInterface<TestResult> *futureInterface
|
||||
= new QFutureInterface<TestResult>(QFutureInterfaceBase::Running);
|
||||
m_futureWatcher.setFuture(futureInterface->future());
|
||||
|
||||
if (useOutputProcessor) {
|
||||
TestOutputReader *outputreader = config->createOutputReader(*futureInterface, nullptr);
|
||||
TestOutputReader *outputreader = config->createOutputReader(nullptr);
|
||||
connect(outputreader, &TestOutputReader::newResult, this, &TestRunner::testResultReady);
|
||||
outputreader->setId(inferior.command.executable().toString());
|
||||
connect(outputreader, &TestOutputReader::newOutputLineAvailable,
|
||||
@@ -641,9 +581,7 @@ void TestRunner::debugTests()
|
||||
this, [outputreader](const QString &msg, OutputFormat format) {
|
||||
processOutput(outputreader, msg, format);
|
||||
});
|
||||
|
||||
connect(runControl, &RunControl::stopped,
|
||||
outputreader, &QObject::deleteLater);
|
||||
connect(runControl, &RunControl::stopped, outputreader, &QObject::deleteLater);
|
||||
}
|
||||
|
||||
m_stopDebugConnect = connect(this, &TestRunner::requestStopTestRun,
|
||||
@@ -706,8 +644,7 @@ void TestRunner::buildProject(Project *project)
|
||||
BuildManager *buildManager = BuildManager::instance();
|
||||
m_buildConnect = connect(this, &TestRunner::requestStopTestRun,
|
||||
buildManager, &BuildManager::cancel);
|
||||
connect(buildManager, &BuildManager::buildQueueFinished,
|
||||
this, &TestRunner::buildFinished);
|
||||
connect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
|
||||
BuildManager::buildProjectWithDependencies(project);
|
||||
if (!BuildManager::isBuilding())
|
||||
buildFinished(false);
|
||||
@@ -717,19 +654,15 @@ void TestRunner::buildFinished(bool success)
|
||||
{
|
||||
disconnect(m_buildConnect);
|
||||
BuildManager *buildManager = BuildManager::instance();
|
||||
disconnect(buildManager, &BuildManager::buildQueueFinished,
|
||||
this, &TestRunner::buildFinished);
|
||||
disconnect(buildManager, &BuildManager::buildQueueFinished, this, &TestRunner::buildFinished);
|
||||
|
||||
if (success) {
|
||||
if (!m_canceled)
|
||||
runOrDebugTests();
|
||||
else if (m_executingTests)
|
||||
onFinished();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
reportResult(ResultType::MessageFatal, Tr::tr("Build failed. Canceling test run."));
|
||||
onFinished();
|
||||
}
|
||||
}
|
||||
|
||||
static RunAfterBuildMode runAfterBuild()
|
||||
{
|
||||
@@ -747,7 +680,7 @@ static RunAfterBuildMode runAfterBuild()
|
||||
|
||||
void TestRunner::onBuildQueueFinished(bool success)
|
||||
{
|
||||
if (m_executingTests || !m_selectedTests.isEmpty()) // paranoia!
|
||||
if (isTestRunning() || !m_selectedTests.isEmpty()) // paranoia!
|
||||
return;
|
||||
|
||||
if (!success || m_runMode != TestRunMode::None)
|
||||
@@ -768,17 +701,15 @@ 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();
|
||||
|
||||
if (m_taskTree)
|
||||
m_taskTree.release()->deleteLater();
|
||||
disconnect(m_stopDebugConnect);
|
||||
disconnect(m_finishDebugConnect);
|
||||
disconnect(m_targetConnect);
|
||||
m_fakeFutureInterface = nullptr;
|
||||
qDeleteAll(m_selectedTests);
|
||||
m_selectedTests.clear();
|
||||
m_cancelTimer.stop();
|
||||
m_runMode = TestRunMode::None;
|
||||
m_executingTests = false;
|
||||
emit testRunFinished();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "autotest_global.h"
|
||||
#include "testresult.h"
|
||||
|
||||
#include "autotestconstants.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFutureWatcher>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -20,13 +19,14 @@ class QLabel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace ProjectExplorer { class Project; }
|
||||
namespace Utils { class QtcProcess; }
|
||||
namespace Utils { class TaskTree; }
|
||||
|
||||
namespace Autotest {
|
||||
|
||||
enum class TestRunMode;
|
||||
class ITestConfiguration;
|
||||
class TestOutputReader;
|
||||
class ITestTreeItem;
|
||||
class TestResult;
|
||||
enum class ResultType;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
void runTests(TestRunMode mode, const QList<ITestConfiguration *> &selectedTests);
|
||||
void runTest(TestRunMode mode, const ITestTreeItem *item);
|
||||
bool isTestRunning() const { return m_executingTests; }
|
||||
bool isTestRunning() const { return m_buildConnect || m_stopDebugConnect || m_taskTree.get(); }
|
||||
|
||||
signals:
|
||||
void testRunStarted();
|
||||
@@ -61,12 +61,7 @@ private:
|
||||
void onFinished();
|
||||
|
||||
int precheckTestConfigurations();
|
||||
bool currentConfigValid();
|
||||
void setUpProcessEnv();
|
||||
void scheduleNext();
|
||||
void cancelCurrent(CancelReason reason);
|
||||
void onProcessDone();
|
||||
void resetInternalPointers();
|
||||
|
||||
void runTestsHelper();
|
||||
void debugTests();
|
||||
@@ -75,14 +70,9 @@ private:
|
||||
bool postponeTestRunWithEmptyExecutable(ProjectExplorer::Project *project);
|
||||
void onBuildSystemUpdated();
|
||||
|
||||
QFutureWatcher<TestResult> m_futureWatcher;
|
||||
QFutureInterface<TestResult> *m_fakeFutureInterface = nullptr;
|
||||
std::unique_ptr<Utils::TaskTree> m_taskTree;
|
||||
|
||||
QList<ITestConfiguration *> m_selectedTests;
|
||||
bool m_executingTests = false;
|
||||
bool m_canceled = false;
|
||||
ITestConfiguration *m_currentConfig = nullptr;
|
||||
Utils::QtcProcess *m_currentProcess = nullptr;
|
||||
TestOutputReader *m_currentOutputReader = nullptr;
|
||||
TestRunMode m_runMode = TestRunMode::None;
|
||||
|
||||
// temporarily used if building before running is necessary
|
||||
|
||||
Reference in New Issue
Block a user