summaryrefslogtreecommitdiff
path: root/plugins/autotest/testrunner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/autotest/testrunner.cpp')
-rw-r--r--plugins/autotest/testrunner.cpp310
1 files changed, 43 insertions, 267 deletions
diff --git a/plugins/autotest/testrunner.cpp b/plugins/autotest/testrunner.cpp
index ec142c9ec0..df9ad92b73 100644
--- a/plugins/autotest/testrunner.cpp
+++ b/plugins/autotest/testrunner.cpp
@@ -15,12 +15,13 @@
** contact form at http://qt.digia.com
**
****************************************************************************/
+#include "testrunner.h"
#include "autotestconstants.h"
#include "autotestplugin.h"
#include "testresultspane.h"
-#include "testrunner.h"
#include "testsettings.h"
+#include "testxmloutputreader.h"
#include <QDebug> // REMOVE
@@ -42,266 +43,12 @@ namespace Autotest {
namespace Internal {
static TestRunner *m_instance = 0;
-static QFutureInterface<void> *m_currentFuture = 0;
-
-TestRunner *TestRunner::instance()
-{
- if (!m_instance)
- m_instance = new TestRunner;
- return m_instance;
-}
-
-TestRunner::TestRunner(QObject *parent) :
- QObject(parent),
- m_building(false),
- m_executingTests(false)
-{
-}
-
-TestRunner::~TestRunner()
-{
- qDeleteAll(m_selectedTests);
- m_selectedTests.clear();
- m_instance = 0;
-}
-
-void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
-{
- qDeleteAll(m_selectedTests);
- m_selectedTests.clear();
- m_selectedTests = selected;
-}
-
-/******************** XML line parser helper ********************/
-
-static bool xmlStartsWith(const QString &code, const QString &start, QString &result)
-{
- if (code.startsWith(start)) {
- result = code.mid(start.length());
- result = result.left(result.indexOf(QLatin1Char('"')));
- result = result.left(result.indexOf(QLatin1String("</")));
- return !result.isEmpty();
- }
- return false;
-}
-
-static bool xmlCData(const QString &code, const QString &start, QString &result)
-{
- if (code.startsWith(start)) {
- int index = code.indexOf(QLatin1String("<![CDATA[")) + 9;
- result = code.mid(index, code.indexOf(QLatin1String("]]>"), index) - index);
- return !result.isEmpty();
- }
- return false;
-}
-
-static bool xmlExtractTypeFileLine(const QString &code, const QString &tagStart,
- Result::Type &result, QString &file, int &line)
-{
- if (code.startsWith(tagStart)) {
- int start = code.indexOf(QLatin1String(" type=\"")) + 7;
- result = TestResult::resultFromString(
- code.mid(start, code.indexOf(QLatin1Char('"'), start) - start));
- start = code.indexOf(QLatin1String(" file=\"")) + 7;
- file = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start);
- start = code.indexOf(QLatin1String(" line=\"")) + 7;
- line = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start).toInt();
- return true;
- }
- return false;
-}
-
-// adapted from qplaintestlogger.cpp
-static QString formatResult(double value)
-{
- if (value < 0 || value == NAN)
- return QLatin1String("NAN");
- if (value == 0)
- return QLatin1String("0");
-
- int significantDigits = 0;
- qreal divisor = 1;
-
- while (value / divisor >= 1) {
- divisor *= 10;
- ++significantDigits;
- }
-
- QString beforeDecimalPoint = QString::number(value, 'f', 0);
- QString afterDecimalPoint = QString::number(value, 'f', 20);
- afterDecimalPoint.remove(0, beforeDecimalPoint.count() + 1);
-
- const int beforeUse = qMin(beforeDecimalPoint.count(), significantDigits);
- const int beforeRemove = beforeDecimalPoint.count() - beforeUse;
-
- beforeDecimalPoint.chop(beforeRemove);
- for (int i = 0; i < beforeRemove; ++i)
- beforeDecimalPoint.append(QLatin1Char('0'));
-
- int afterUse = significantDigits - beforeUse;
- if (beforeDecimalPoint == QLatin1String("0") && !afterDecimalPoint.isEmpty()) {
- ++afterUse;
- int i = 0;
- while (i < afterDecimalPoint.count() && afterDecimalPoint.at(i) == QLatin1Char('0'))
- ++i;
- afterUse += i;
- }
-
- const int afterRemove = afterDecimalPoint.count() - afterUse;
- afterDecimalPoint.chop(afterRemove);
-
- QString result = beforeDecimalPoint;
- if (afterUse > 0)
- result.append(QLatin1Char('.'));
- result += afterDecimalPoint;
-
- return result;
-}
-
-static bool xmlExtractBenchmarkInformation(const QString &code, const QString &tagStart,
- QString &description)
-{
- if (code.startsWith(tagStart)) {
- int start = code.indexOf(QLatin1String(" metric=\"")) + 9;
- const QString metric = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start);
- start = code.indexOf(QLatin1String(" value=\"")) + 8;
- const double value = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start).toDouble();
- start = code.indexOf(QLatin1String(" iterations=\"")) + 13;
- const int iterations = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start).toInt();
- QString metricsText;
- if (metric == QLatin1String("WalltimeMilliseconds")) // default
- metricsText = QLatin1String("msecs");
- else if (metric == QLatin1String("CPUTicks")) // -tickcounter
- metricsText = QLatin1String("CPU ticks");
- else if (metric == QLatin1String("Events")) // -eventcounter
- metricsText = QLatin1String("events");
- else if (metric == QLatin1String("InstructionReads")) // -callgrind
- metricsText = QLatin1String("instruction reads");
- else if (metric == QLatin1String("CPUCycles")) // -perf
- metricsText = QLatin1String("CPU cycles");
- description = QObject::tr("%1 %2 per iteration (total: %3, iterations: %4)")
- .arg(formatResult(value))
- .arg(metricsText)
- .arg(formatResult(value * (double)iterations))
- .arg(iterations);
- return true;
- }
- return false;
-}
void emitTestResultCreated(const TestResult &testResult)
{
emit m_instance->testResultCreated(testResult);
}
-/****************** XML line parser helper end ******************/
-
-void processOutput(QProcess *testRunner)
-{
- if (!testRunner)
- return;
- static QString className;
- static QString testCase;
- static QString dataTag;
- static Result::Type result = Result::UNKNOWN;
- static QString description;
- static QString file;
- static int lineNumber = 0;
- static QString duration;
- static bool readingDescription = false;
- static QString qtVersion;
- static QString qtestVersion;
- static QString benchmarkDescription;
-
- while (testRunner->canReadLine()) {
- // TODO Qt5 uses UTF-8 - while Qt4 uses ISO-8859-1 - could this be a problem?
- const QString line = QString::fromUtf8(testRunner->readLine()).trimmed();
- if (line.isEmpty() || line.startsWith(QLatin1String("<?xml version"))) {
- className = QString();
- continue;
- }
- if (xmlStartsWith(line, QLatin1String("<TestCase name=\""), className))
- continue;
- if (xmlStartsWith(line, QLatin1String("<TestFunction name=\""), testCase)) {
- dataTag = QString();
- description = QString();
- duration = QString();
- file = QString();
- result = Result::UNKNOWN;
- lineNumber = 0;
- readingDescription = false;
- emitTestResultCreated(
- TestResult(QString(), QString(), QString(), Result::MESSAGE_CURRENT_TEST,
- QObject::tr("Entering Test Function %1::%2")
- .arg(className).arg(testCase)));
- continue;
- }
- if (xmlStartsWith(line, QLatin1String("<Duration msecs=\""), duration)) {
- continue;
- }
- if (xmlExtractTypeFileLine(line, QLatin1String("<Message"), result, file, lineNumber))
- continue;
- if (xmlCData(line, QLatin1String("<DataTag>"), dataTag))
- continue;
- if (xmlCData(line, QLatin1String("<Description>"), description)) {
- if (!line.endsWith(QLatin1String("</Description>")))
- readingDescription = true;
- continue;
- }
- if (xmlExtractTypeFileLine(line, QLatin1String("<Incident"), result, file, lineNumber)) {
- if (line.endsWith(QLatin1String("/>"))) {
- TestResult testResult(className, testCase, dataTag, result, description);
- if (!file.isEmpty())
- file = QFileInfo(testRunner->workingDirectory(), file).canonicalFilePath();
- testResult.setFileName(file);
- testResult.setLine(lineNumber);
- emitTestResultCreated(testResult);
- }
- continue;
- }
- if (xmlExtractBenchmarkInformation(line, QLatin1String("<BenchmarkResult"), benchmarkDescription)) {
- TestResult testResult(className, testCase, dataTag, Result::BENCHMARK, benchmarkDescription);
- emitTestResultCreated(testResult);
- continue;
- }
- if (line == QLatin1String("</Message>") || line == QLatin1String("</Incident>")) {
- TestResult testResult(className, testCase, dataTag, result, description);
- if (!file.isEmpty())
- file = QFileInfo(testRunner->workingDirectory(), file).canonicalFilePath();
- testResult.setFileName(file);
- testResult.setLine(lineNumber);
- emitTestResultCreated(testResult);
- description = QString();
- } else if (line == QLatin1String("</TestFunction>") && !duration.isEmpty()) {
- TestResult testResult(className, testCase, QString(), Result::MESSAGE_INTERNAL,
- QObject::tr("execution took %1ms").arg(duration));
- emitTestResultCreated(testResult);
- m_currentFuture->setProgressValue(m_currentFuture->progressValue() + 1);
- } else if (line == QLatin1String("</TestCase>") && !duration.isEmpty()) {
- TestResult testResult(className, QString(), QString(), Result::MESSAGE_INTERNAL,
- QObject::tr("Test execution took %1ms").arg(duration));
- emitTestResultCreated(testResult);
- } else if (readingDescription) {
- if (line.endsWith(QLatin1String("]]></Description>"))) {
- description.append(QLatin1Char('\n'));
- description.append(line.left(line.indexOf(QLatin1String("]]></Description>"))));
- readingDescription = false;
- } else {
- description.append(QLatin1Char('\n'));
- description.append(line);
- }
- } else if (xmlStartsWith(line, QLatin1String("<QtVersion>"), qtVersion)) {
- emitTestResultCreated(FaultyTestResult(Result::MESSAGE_INTERNAL,
- QObject::tr("Qt Version: %1").arg(qtVersion)));
- } else if (xmlStartsWith(line, QLatin1String("<QTestVersion>"), qtestVersion)) {
- emitTestResultCreated(FaultyTestResult(Result::MESSAGE_INTERNAL,
- QObject::tr("QTest Version: %1").arg(qtestVersion)));
- } else {
-// qDebug() << "Unhandled line:" << line; // TODO remove
- }
- }
-}
-
static QString executableFilePath(const QString &command, const QProcessEnvironment &environment)
{
if (command.isEmpty())
@@ -331,29 +78,60 @@ static QString executableFilePath(const QString &command, const QProcessEnvironm
return QString();
}
-void performTestRun(QFutureInterface<void> &future, const QList<TestConfiguration *> selectedTests, const int timeout, const QString metricsOption, TestRunner* testRunner)
+TestRunner *TestRunner::instance()
+{
+ if (!m_instance)
+ m_instance = new TestRunner;
+ return m_instance;
+}
+
+TestRunner::TestRunner(QObject *parent) :
+ QObject(parent),
+ m_building(false),
+ m_executingTests(false)
+{
+}
+
+TestRunner::~TestRunner()
+{
+ qDeleteAll(m_selectedTests);
+ m_selectedTests.clear();
+ m_instance = 0;
+}
+
+void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
+{
+ qDeleteAll(m_selectedTests);
+ m_selectedTests.clear();
+ m_selectedTests = selected;
+}
+
+void performTestRun(QFutureInterface<void> &futureInterface, const QList<TestConfiguration *> selectedTests, const int timeout, const QString metricsOption, TestRunner* testRunner)
{
int testCaseCount = 0;
foreach (const TestConfiguration *config, selectedTests)
testCaseCount += config->testCaseCount();
- m_currentFuture = &future;
QProcess testProcess;
testProcess.setReadChannelMode(QProcess::MergedChannels);
testProcess.setReadChannel(QProcess::StandardOutput);
QObject::connect(testRunner, &TestRunner::requestStopTestRun, &testProcess, [&] () {
- future.cancel(); // this kills the process if that is still in the running loop
+ futureInterface.cancel(); // this kills the process if that is still in the running loop
});
- QObject::connect(&testProcess, &QProcess::readyReadStandardOutput, [&] () {
- processOutput(&testProcess);
+ TestXmlOutputReader xmlReader(&testProcess);
+ QObject::connect(&xmlReader, &TestXmlOutputReader::increaseProgress, [&] () {
+ futureInterface.setProgressValue(futureInterface.progressValue() + 1);
});
+ QObject::connect(&xmlReader, &TestXmlOutputReader::testResultCreated, &emitTestResultCreated);
+
+ QObject::connect(&testProcess, &QProcess::readyRead, &xmlReader, &TestXmlOutputReader::processOutput);
- m_currentFuture->setProgressRange(0, testCaseCount);
- m_currentFuture->setProgressValue(0);
+ futureInterface.setProgressRange(0, testCaseCount);
+ futureInterface.setProgressValue(0);
foreach (const TestConfiguration *testConfiguration, selectedTests) {
- if (m_currentFuture->isCanceled())
+ if (futureInterface.isCanceled())
break;
QProcessEnvironment environment = testConfiguration->environment().toProcessEnvironment();
@@ -381,7 +159,7 @@ void performTestRun(QFutureInterface<void> &future, const QList<TestConfiguratio
executionTimer.start();
if (ok) {
while (testProcess.state() == QProcess::Running && executionTimer.elapsed() < timeout) {
- if (m_currentFuture->isCanceled()) {
+ if (futureInterface.isCanceled()) {
testProcess.kill();
testProcess.waitForFinished();
emitTestResultCreated(FaultyTestResult(Result::MESSAGE_FATAL,
@@ -400,9 +178,7 @@ void performTestRun(QFutureInterface<void> &future, const QList<TestConfiguratio
}
}
}
- m_currentFuture->setProgressValue(testCaseCount);
-
- m_currentFuture = 0;
+ futureInterface.setProgressValue(testCaseCount);
}
void TestRunner::runTests()