From 3b423602eddd36380a98b2e2388ceabdd80a3db1 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 19 Jan 2016 14:24:09 +0100 Subject: [PATCH] Report test results through QFutureInterface Change-Id: Id5704ff2744ee925978c518f44069b26c66d682e Reviewed-by: Christian Stenger --- plugins/autotest/testoutputreader.cpp | 45 ++++++++------- plugins/autotest/testoutputreader.h | 18 ++---- plugins/autotest/testresultspane.cpp | 2 + plugins/autotest/testrunner.cpp | 83 ++++++++++----------------- plugins/autotest/testrunner.h | 4 +- 5 files changed, 64 insertions(+), 88 deletions(-) diff --git a/plugins/autotest/testoutputreader.cpp b/plugins/autotest/testoutputreader.cpp index 4ebfed8e7c0..319f8d7ee3e 100644 --- a/plugins/autotest/testoutputreader.cpp +++ b/plugins/autotest/testoutputreader.cpp @@ -131,10 +131,15 @@ static QString constructBenchmarkInformation(const QString &metric, double value .arg(iterations); } -TestOutputReader::TestOutputReader(QProcess *testApplication, OutputType type) - : m_testApplication(testApplication) - , m_type(type) +TestOutputReader::TestOutputReader(QFutureInterface futureInterface, + QProcess *testApplication, TestType type) + : m_testApplication(testApplication), + m_futureInterface(futureInterface) { + if (type == TestTypeQt) + connect(testApplication, &QProcess::readyRead, this, &TestOutputReader::processOutput); + else + connect(testApplication, &QProcess::readyRead, this, &TestOutputReader::processGTestOutput); } enum CDATAMode { @@ -186,7 +191,7 @@ void TestOutputReader::processOutput() auto testResult = new QTestResult(className); testResult->setResult(Result::MessageTestCaseStart); testResult->setDescription(tr("Executing test case %1").arg(className)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } else if (currentTag == QStringLiteral("TestFunction")) { testCase = xmlReader.attributes().value(QStringLiteral("name")).toString(); QTC_ASSERT(!testCase.isEmpty(), continue); @@ -194,7 +199,7 @@ void TestOutputReader::processOutput() testResult->setResult(Result::MessageCurrentTest); testResult->setDescription(tr("Entering test function %1::%2").arg(className, testCase)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } else if (currentTag == QStringLiteral("Duration")) { duration = xmlReader.attributes().value(QStringLiteral("msecs")).toString(); QTC_ASSERT(!duration.isEmpty(), continue); @@ -280,16 +285,16 @@ void TestOutputReader::processOutput() testResult->setTestCase(testCase); testResult->setResult(Result::MessageInternal); testResult->setDescription(tr("Execution took %1 ms.").arg(duration)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } - emit increaseProgress(); + m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); } else if (currentTag == QStringLiteral("TestCase")) { auto testResult = new QTestResult(className); testResult->setResult(Result::MessageTestCaseEnd); testResult->setDescription( duration.isEmpty() ? tr("Test finished.") : tr("Test execution took %1 ms.").arg(duration)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } else if (validEndTags.contains(currentTag.toString())) { auto testResult = new QTestResult(className); testResult->setTestCase(testCase); @@ -298,7 +303,7 @@ void TestOutputReader::processOutput() testResult->setFileName(file); testResult->setLine(lineNumber); testResult->setDescription(description); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } break; } @@ -348,7 +353,7 @@ void TestOutputReader::processGTestOutput() auto testResult = new GTestResult(); testResult->setResult(Result::MessageInternal); testResult->setDescription(line); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); description.clear(); } else if (disabledTests.exactMatch(line)) { auto testResult = new GTestResult(); @@ -356,7 +361,7 @@ void TestOutputReader::processGTestOutput() int disabled = disabledTests.cap(1).toInt(); testResult->setDescription(tr("You have %n disabled test(s).", 0, disabled)); testResult->setLine(disabled); // misuse line property to hold number of disabled - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); description.clear(); } continue; @@ -367,7 +372,7 @@ void TestOutputReader::processGTestOutput() testResult->setTestCase(currentTestSet); testResult->setResult(Result::MessageTestCaseEnd); testResult->setDescription(tr("Test execution took %1").arg(testEnds.cap(2))); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); currentTestName.clear(); currentTestSet.clear(); } else if (newTestStarts.exactMatch(line)) { @@ -375,24 +380,24 @@ void TestOutputReader::processGTestOutput() auto testResult = new GTestResult(currentTestName); testResult->setResult(Result::MessageTestCaseStart); testResult->setDescription(tr("Executing test case %1").arg(currentTestName)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } else if (newTestSetStarts.exactMatch(line)) { currentTestSet = newTestSetStarts.cap(1); auto testResult = new GTestResult(); testResult->setResult(Result::MessageCurrentTest); testResult->setDescription(tr("Entering test set %1").arg(currentTestSet)); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); } else if (testSetSuccess.exactMatch(line)) { auto testResult = new GTestResult(currentTestName); testResult->setTestCase(currentTestSet); testResult->setResult(Result::Pass); - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); testResult = new GTestResult(currentTestName); testResult->setTestCase(currentTestSet); testResult->setResult(Result::MessageInternal); testResult->setDescription(tr("Execution took %1.").arg(testSetSuccess.cap(2))); - testResultCreated(testResult); - emit increaseProgress(); + m_futureInterface.reportResult(testResult); + m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); } else if (testSetFail.exactMatch(line)) { auto testResult = new GTestResult(currentTestName); testResult->setTestCase(currentTestSet); @@ -409,14 +414,14 @@ void TestOutputReader::processGTestOutput() testResult->setFileName(file); testResult->setLine(line.toInt()); } - testResultCreated(testResult); + m_futureInterface.reportResult(testResult); description.clear(); testResult = new GTestResult(currentTestName); testResult->setTestCase(currentTestSet); testResult->setResult(Result::MessageInternal); testResult->setDescription(tr("Execution took %1.").arg(testSetFail.cap(2))); - testResultCreated(testResult); - emit increaseProgress(); + m_futureInterface.reportResult(testResult); + m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); } } } diff --git a/plugins/autotest/testoutputreader.h b/plugins/autotest/testoutputreader.h index 532e081a03f..8c9fd789b60 100644 --- a/plugins/autotest/testoutputreader.h +++ b/plugins/autotest/testoutputreader.h @@ -22,6 +22,7 @@ #include "testresult.h" +#include #include #include @@ -36,24 +37,15 @@ class TestOutputReader : public QObject { Q_OBJECT public: - enum OutputType { - Qt, - GTest - }; + TestOutputReader(QFutureInterface futureInterface, + QProcess *testApplication, TestType type); - TestOutputReader(QProcess *testApplication, OutputType type = Qt); - -public slots: +private: void processOutput(); void processGTestOutput(); -signals: - void testResultCreated(TestResult *testResult); - void increaseProgress(); - -private: QProcess *m_testApplication; // not owned - OutputType m_type; + QFutureInterface m_futureInterface; }; } // namespace Internal diff --git a/plugins/autotest/testresultspane.cpp b/plugins/autotest/testresultspane.cpp index 3e2863a7417..0114279c555 100644 --- a/plugins/autotest/testresultspane.cpp +++ b/plugins/autotest/testresultspane.cpp @@ -128,6 +128,8 @@ TestResultsPane::TestResultsPane(QObject *parent) : this, &TestResultsPane::onTestRunStarted); connect(TestRunner::instance(), &TestRunner::testRunFinished, this, &TestResultsPane::onTestRunFinished); + connect(TestRunner::instance(), &TestRunner::testResultReady, + this, &TestResultsPane::addTestResult); } void TestResultsPane::createToolButtons() diff --git a/plugins/autotest/testrunner.cpp b/plugins/autotest/testrunner.cpp index cde0dde4bfb..e23d4b58b26 100644 --- a/plugins/autotest/testrunner.cpp +++ b/plugins/autotest/testrunner.cpp @@ -36,7 +36,6 @@ #include #include -#include #include namespace Autotest { @@ -44,11 +43,6 @@ namespace Internal { static TestRunner *m_instance = 0; -static void emitTestResultCreated(TestResult *testResult) -{ - emit m_instance->testResultCreated(testResult); -} - static QString executableFilePath(const QString &command, const QProcessEnvironment &environment) { if (command.isEmpty()) @@ -89,6 +83,17 @@ TestRunner::TestRunner(QObject *parent) : QObject(parent), m_executingTests(false) { + connect(&m_futureWatcher, &QFutureWatcher::resultReadyAt, + this, [this](int index) { emit testResultReady(m_futureWatcher.resultAt(index)); }); + connect(&m_futureWatcher, &QFutureWatcher::finished, + this, &TestRunner::onFinished); + connect(this, &TestRunner::requestStopTestRun, + &m_futureWatcher, &QFutureWatcher::cancel); + connect(&m_futureWatcher, &QFutureWatcher::canceled, + this, [this]() { emit testResultReady(new FaultyTestResult( + Result::MessageFatal, + QObject::tr("Test run canceled by user."))); + }); } TestRunner::~TestRunner() @@ -105,7 +110,7 @@ void TestRunner::setSelectedTests(const QList &selected) m_selectedTests = selected; } -static void performTestRun(QFutureInterface &futureInterface, +static void performTestRun(QFutureInterface &futureInterface, const QList selectedTests, const int timeout, const QString metricsOption) { @@ -116,7 +121,7 @@ static void performTestRun(QFutureInterface &futureInterface, if (config->project()) { testCaseCount += config->testCaseCount(); } else { - emitTestResultCreated(new FaultyTestResult(Result::MessageWarn, + futureInterface.reportResult(new FaultyTestResult(Result::MessageWarn, QObject::tr("Project is null for \"%1\". Removing from test run.\n" "Check the test environment.").arg(config->displayName()))); } @@ -126,28 +131,12 @@ static void performTestRun(QFutureInterface &futureInterface, testProcess.setReadChannelMode(QProcess::MergedChannels); testProcess.setReadChannel(QProcess::StandardOutput); - TestOutputReader outputReader(&testProcess); - QObject::connect(&outputReader, &TestOutputReader::increaseProgress, [&] () { - futureInterface.setProgressValue(futureInterface.progressValue() + 1); - }); - QObject::connect(&outputReader, &TestOutputReader::testResultCreated, &emitTestResultCreated); - futureInterface.setProgressRange(0, testCaseCount); futureInterface.setProgressValue(0); - QMetaObject::Connection connection; foreach (const TestConfiguration *testConfiguration, selectedTests) { - if (connection) - QObject::disconnect(connection); - - TestType testType = testConfiguration->testType(); - if (testType == TestTypeQt) { - connection = QObject::connect(&testProcess, &QProcess::readyRead, &outputReader, - &TestOutputReader::processOutput); - } else { - connection = QObject::connect(&testProcess, &QProcess::readyRead, &outputReader, - &TestOutputReader::processGTestOutput); - } + TestOutputReader outputReader(futureInterface, &testProcess, testConfiguration->testType()); + Q_UNUSED(outputReader); if (futureInterface.isCanceled()) break; @@ -157,14 +146,14 @@ static void performTestRun(QFutureInterface &futureInterface, QProcessEnvironment environment = testConfiguration->environment().toProcessEnvironment(); QString commandFilePath = executableFilePath(testConfiguration->targetFile(), environment); if (commandFilePath.isEmpty()) { - emitTestResultCreated(new FaultyTestResult(Result::MessageFatal, + futureInterface.reportResult(new FaultyTestResult(Result::MessageFatal, QObject::tr("Could not find command \"%1\". (%2)") .arg(testConfiguration->targetFile()) .arg(testConfiguration->displayName()))); continue; } - if (testType == TestTypeQt) { + if (testConfiguration->testType() == TestTypeQt) { QStringList argumentList(QLatin1String("-xml")); if (!metricsOption.isEmpty()) argumentList << metricsOption; @@ -196,8 +185,7 @@ static void performTestRun(QFutureInterface &futureInterface, if (futureInterface.isCanceled()) { testProcess.kill(); testProcess.waitForFinished(); - emitTestResultCreated(new FaultyTestResult(Result::MessageFatal, - QObject::tr("Test run canceled by user."))); + return; } eventLoop.processEvents(); } @@ -207,7 +195,7 @@ static void performTestRun(QFutureInterface &futureInterface, if (testProcess.state() != QProcess::NotRunning) { testProcess.kill(); testProcess.waitForFinished(); - emitTestResultCreated(new FaultyTestResult(Result::MessageFatal, QObject::tr( + futureInterface.reportResult(new FaultyTestResult(Result::MessageFatal, QObject::tr( "Test case canceled due to timeout. \nMaybe raise the timeout?"))); } } @@ -227,14 +215,14 @@ void TestRunner::prepareToRunTests() foreach (TestConfiguration *config, m_selectedTests) { if (!omitRunConfigWarnings && config->guessedConfiguration()) { - TestResultsPane::instance()->addTestResult(new FaultyTestResult(Result::MessageWarn, + emit testResultReady(new FaultyTestResult(Result::MessageWarn, tr("Project's run configuration was guessed for \"%1\".\n" "This might cause trouble during execution.").arg(config->displayName()))); } } if (m_selectedTests.empty()) { - TestResultsPane::instance()->addTestResult(new FaultyTestResult(Result::MessageWarn, + emit testResultReady(new FaultyTestResult(Result::MessageWarn, tr("No tests selected. Canceling test run."))); onFinished(); return; @@ -242,7 +230,7 @@ void TestRunner::prepareToRunTests() ProjectExplorer::Project *project = m_selectedTests.at(0)->project(); if (!project) { - TestResultsPane::instance()->addTestResult(new FaultyTestResult(Result::MessageWarn, + emit testResultReady(new FaultyTestResult(Result::MessageWarn, tr("Project is null. Canceling test run.\n" "Only desktop kits are supported. Make sure the " "currently active kit is a desktop kit."))); @@ -258,7 +246,7 @@ void TestRunner::prepareToRunTests() if (project->hasActiveBuildSettings()) { buildProject(project); } else { - TestResultsPane::instance()->addTestResult(new FaultyTestResult(Result::MessageFatal, + emit testResultReady(new FaultyTestResult(Result::MessageFatal, tr("Project is not configured. Canceling test run."))); onFinished(); return; @@ -271,20 +259,10 @@ void TestRunner::runTests() const QSharedPointer settings = AutotestPlugin::instance()->settings(); const QString &metricsOption = TestSettings::metricsTypeToOption(settings->metrics); - connect(this, &TestRunner::testResultCreated, - TestResultsPane::instance(), &TestResultsPane::addTestResult, - Qt::QueuedConnection); - - QFuture future = Utils::runAsync(&performTestRun, m_selectedTests, settings->timeout, - metricsOption); - - Core::FutureProgress *progress = Core::ProgressManager::addTask(future, tr("Running Tests"), - Autotest::Constants::TASK_INDEX); - connect(progress, &Core::FutureProgress::finished, - TestRunner::instance(), &TestRunner::onFinished); - connect(this, &TestRunner::requestStopTestRun, progress, [progress]() { - progress->future().cancel(); - }); + QFuture future = Utils::runAsync(&performTestRun, m_selectedTests, + settings->timeout, metricsOption); + m_futureWatcher.setFuture(future); + Core::ProgressManager::addTask(future, tr("Running Tests"), Autotest::Constants::TASK_INDEX); } void TestRunner::buildProject(ProjectExplorer::Project *project) @@ -307,17 +285,14 @@ void TestRunner::buildFinished(bool success) if (success) { runTests(); } else { - TestResultsPane::instance()->addTestResult(new FaultyTestResult(Result::MessageFatal, - tr("Build failed. Canceling test run."))); + emit testResultReady(new FaultyTestResult(Result::MessageFatal, + tr("Build failed. Canceling test run."))); onFinished(); } } void TestRunner::onFinished() { - disconnect(this, &TestRunner::testResultCreated, - TestResultsPane::instance(), &TestResultsPane::addTestResult); - m_executingTests = false; emit testRunFinished(); } diff --git a/plugins/autotest/testrunner.h b/plugins/autotest/testrunner.h index e03a0c3d4d5..c3cd89ef60d 100644 --- a/plugins/autotest/testrunner.h +++ b/plugins/autotest/testrunner.h @@ -23,6 +23,7 @@ #include "testconfiguration.h" #include "testresult.h" +#include #include #include @@ -47,8 +48,8 @@ public: signals: void testRunStarted(); void testRunFinished(); - void testResultCreated(TestResult *testResult); void requestStopTestRun(); + void testResultReady(TestResult *result); public slots: void prepareToRunTests(); @@ -62,6 +63,7 @@ private: void runTests(); explicit TestRunner(QObject *parent = 0); + QFutureWatcher m_futureWatcher; QList m_selectedTests; bool m_executingTests;