summaryrefslogtreecommitdiff
path: root/plugins/autotest
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@digia.com>2014-11-04 12:35:00 +0100
committerChristian Stenger <christian.stenger@theqtcompany.com>2014-12-04 13:52:15 +0100
commitd5913658b2d0f2bd04a5522e32bc8496414cb0ed (patch)
tree8af3a13d88a2d3d7aea83e4bb2369ab06f5a4c4f /plugins/autotest
parent431d15bd0caa70588c7118ef8c0b32850d08ab82 (diff)
downloadqt-creator-d5913658b2d0f2bd04a5522e32bc8496414cb0ed.tar.gz
Perform test execution in separate thread and add progress bar
Diffstat (limited to 'plugins/autotest')
-rw-r--r--plugins/autotest/autotestconstants.h1
-rw-r--r--plugins/autotest/testrunner.cpp292
-rw-r--r--plugins/autotest/testrunner.h7
3 files changed, 158 insertions, 142 deletions
diff --git a/plugins/autotest/autotestconstants.h b/plugins/autotest/autotestconstants.h
index df3e26be11..3cfc770d45 100644
--- a/plugins/autotest/autotestconstants.h
+++ b/plugins/autotest/autotestconstants.h
@@ -26,6 +26,7 @@ const char ACTION_ID[] = "AutoTest.Action";
const char MENU_ID[] = "AutoTest.Menu";
const char AUTOTEST_ID[] = "AutoTest.ATP";
const char AUTOTEST_CONTEXT[] = "Auto Tests";
+const char TASK_INDEX[] = "AutoTest.Task.Index";
} // namespace Autotest
} // namespace Constants
diff --git a/plugins/autotest/testrunner.cpp b/plugins/autotest/testrunner.cpp
index ded6fab9f1..eae9d4c58a 100644
--- a/plugins/autotest/testrunner.cpp
+++ b/plugins/autotest/testrunner.cpp
@@ -16,22 +16,32 @@
**
****************************************************************************/
+#include "autotestconstants.h"
#include "testresultspane.h"
#include "testrunner.h"
#include <QDebug> // REMOVE
+#include <coreplugin/progressmanager/futureprogress.h>
+#include <coreplugin/progressmanager/progressmanager.h>
+
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
+#include <utils/multitask.h>
+
+#include <QFuture>
+#include <QFutureInterface>
#include <QTime>
namespace Autotest {
namespace Internal {
-static TestRunner* m_instance = 0;
+static TestRunner *m_instance = 0;
+static QProcess *m_runner = 0;
+static QFutureInterface<void> *m_currentFuture = 0;
TestRunner *TestRunner::instance()
{
@@ -44,13 +54,6 @@ TestRunner::TestRunner(QObject *parent) :
QObject(parent),
m_building(false)
{
- m_runner.setReadChannelMode(QProcess::MergedChannels);
- m_runner.setReadChannel(QProcess::StandardOutput);
-
- connect(&m_runner, &QProcess::readyReadStandardOutput,
- this, &TestRunner::processOutput, Qt::DirectConnection);
- connect(&m_runner, SIGNAL(finished(int,QProcess::ExitStatus)),
- this, SLOT(onRunnerFinished(int,QProcess::ExitStatus)), Qt::DirectConnection);
}
TestRunner::~TestRunner()
@@ -58,6 +61,8 @@ TestRunner::~TestRunner()
qDeleteAll(m_selectedTests);
m_selectedTests.clear();
m_instance = 0;
+ if (m_runner)
+ delete m_runner;
}
void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
@@ -67,80 +72,6 @@ void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
m_selectedTests = selected;
}
-void TestRunner::runTests()
-{
- if (m_selectedTests.empty()) {
- TestResultsPane::instance()->addTestResult(
- TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
- tr("*** No tests selected - canceling Test Run ***")));
- return;
- }
-
- ProjectExplorer::Project *project = m_selectedTests.at(0)->project();
-
- if (!project) {// add a warning or info to output? possible at all?
- return;
- }
-
- ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
- ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings();
- if (pes.buildBeforeDeploy) {
- if (!project->hasActiveBuildSettings()) {
- TestResultsPane::instance()->addTestResult(
- TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
- tr("*** Project is not configured - canceling Test Run ***")));
- return;
- }
- buildProject(project);
- while (m_building) {
- qApp->processEvents();
- }
-
- if (!m_buildSucceeded) {
- TestResultsPane::instance()->addTestResult(
- TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
- tr("*** Build failed - canceling Test Run ***")));
- return;
- }
- }
-
- int testCaseCount = 0;
- foreach (const TestConfiguration *config, m_selectedTests)
- testCaseCount += config->testCaseCount();
-
- // clear old log and output pane
- m_xmlLog.clear();
- TestResultsPane::instance()->clearContents();
-
- emit testRunStarted();
-
- foreach (TestConfiguration *tc, m_selectedTests) {
- QString cmd = tc->targetFile();
- QString workDir = tc->workingDirectory();
- QStringList args;
- Utils::Environment env = tc->environment();
-
- args << QLatin1String("-xml");
- if (tc->testCases().count())
- args << tc->testCases();
-
- exec(cmd, args, workDir, env);
- }
- qDebug("test run finished");
- emit testRunFinished();
-}
-
-void TestRunner::stopTestRun()
-{
- if (m_runner.state() != QProcess::NotRunning) {
- m_runner.kill();
- m_runner.waitForFinished();
- TestResultsPane::instance()->addTestResult(
- TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
- tr("*** Test Run canceled by user ***")));
- }
-}
-
/******************** XML line parser helper ********************/
static bool xmlStartsWith(const QString &code, const QString &start, QString &result)
@@ -182,8 +113,10 @@ static bool xmlExtractTypeFileLine(const QString &code, const QString &tagStart,
/****************** XML line parser helper end ******************/
-void TestRunner::processOutput()
+void processOutput()
{
+ if (!m_runner)
+ return;
static QString className;
static QString testCase;
static QString dataTag;
@@ -196,11 +129,9 @@ void TestRunner::processOutput()
static QString qtVersion;
static QString qtestVersion;
- while (m_runner.canReadLine()) {
+ while (m_runner->canReadLine()) {
// TODO Qt5 uses UTF-8 - while Qt4 uses ISO-8859-1 - could this be a problem?
- QString line = QString::fromUtf8(m_runner.readLine());
- line = line.trimmed();
- m_xmlLog.append(line);
+ const QString line = QString::fromUtf8(m_runner->readLine()).trimmed();
if (line.isEmpty() || line.startsWith(QLatin1String("<?xml version"))) {
className = QString();
continue;
@@ -233,7 +164,7 @@ void TestRunner::processOutput()
if (line.endsWith(QLatin1String("/>"))) {
TestResult testResult(className, testCase, dataTag, result, description);
if (!file.isEmpty())
- file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath();
+ file = QFileInfo(m_runner->workingDirectory(), file).canonicalFilePath();
testResult.setFileName(file);
testResult.setLine(lineNumber);
TestResultsPane::instance()->addTestResult(testResult);
@@ -243,18 +174,19 @@ void TestRunner::processOutput()
if (line == QLatin1String("</Message>") || line == QLatin1String("</Incident>")) {
TestResult testResult(className, testCase, dataTag, result, description);
if (!file.isEmpty())
- file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath();
+ file = QFileInfo(m_runner->workingDirectory(), file).canonicalFilePath();
testResult.setFileName(file);
testResult.setLine(lineNumber);
TestResultsPane::instance()->addTestResult(testResult);
description = QString();
} else if (line == QLatin1String("</TestFunction>") && !duration.isEmpty()) {
TestResult testResult(className, testCase, QString(), ResultType::MESSAGE_INTERNAL,
- tr("execution took %1ms").arg(duration));
+ QObject::tr("execution took %1ms").arg(duration));
TestResultsPane::instance()->addTestResult(testResult);
+ m_currentFuture->setProgressValue(m_currentFuture->progressValue() + 1);
} else if (line == QLatin1String("</TestCase>") && !duration.isEmpty()) {
TestResult testResult(className, QString(), QString(), ResultType::MESSAGE_INTERNAL,
- tr("Test execution took %1ms").arg(duration));
+ QObject::tr("Test execution took %1ms").arg(duration));
TestResultsPane::instance()->addTestResult(testResult);
} else if (readingDescription) {
if (line.endsWith(QLatin1String("]]></Description>"))) {
@@ -268,44 +200,17 @@ void TestRunner::processOutput()
} else if (xmlStartsWith(line, QLatin1String("<QtVersion>"), qtVersion)) {
TestResultsPane::instance()->addTestResult(
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL,
- tr("Qt Version: %1").arg(qtVersion)));
+ QObject::tr("Qt Version: %1").arg(qtVersion)));
} else if (xmlStartsWith(line, QLatin1String("<QTestVersion>"), qtestVersion)) {
TestResultsPane::instance()->addTestResult(
TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL,
- tr("QTest Version: %1").arg(qtestVersion)));
+ QObject::tr("QTest Version: %1").arg(qtestVersion)));
} else {
// qDebug() << "Unhandled line:" << line; // TODO remove
}
}
}
-void TestRunner::onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus)
-{
- qDebug("runnerFinished");
-}
-
-void TestRunner::buildProject(ProjectExplorer::Project *project)
-{
- m_building = true;
- m_buildSucceeded = false;
- ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
- ProjectExplorer::BuildManager::instance());
- ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
- pep->buildProject(project);
- connect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
-}
-
-void TestRunner::buildFinished(bool success)
-{
- ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
- ProjectExplorer::BuildManager::instance());
- disconnect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
- this, &TestRunner::buildFinished);
- m_building = false;
- m_buildSucceeded = success;
-}
-
static QString which(const QString &path, const QString &cmd)
{
if (path.isEmpty() || cmd.isEmpty())
@@ -319,7 +224,7 @@ static QString which(const QString &path, const QString &cmd)
#endif
foreach (const QString p, paths) {
- QString fName = p + QDir::separator() + cmd;
+ const QString fName = p + QDir::separator() + cmd;
QFileInfo fi(fName);
if (fi.exists() && fi.isExecutable())
return fName;
@@ -338,13 +243,9 @@ static QString which(const QString &path, const QString &cmd)
return QString();
}
-bool TestRunner::exec(const QString &cmd, const QStringList &args, const QString &workingDir,
- const Utils::Environment &env, int timeout)
+bool performExec(const QString &cmd, const QStringList &args, const QString &workingDir,
+ const Utils::Environment &env, int timeout = 60000)
{
- if (m_runner.state() != QProcess::NotRunning) { // kill the runner if it's running already
- m_runner.kill();
- m_runner.waitForFinished();
- }
QString runCmd;
if (!QDir::toNativeSeparators(cmd).contains(QDir::separator())) {
if (env.hasKey(QLatin1String("PATH")))
@@ -354,33 +255,154 @@ bool TestRunner::exec(const QString &cmd, const QStringList &args, const QString
}
if (runCmd.isEmpty()) {
- qDebug("Could not find cmd...");
+ TestResultsPane::instance()->addTestResult(
+ TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
+ QObject::tr("*** Could not find command '%1' ***").arg(cmd)));
return false;
}
- m_runner.setWorkingDirectory(workingDir);
- m_runner.setProcessEnvironment(env.toProcessEnvironment());
+ m_runner->setWorkingDirectory(workingDir);
+ m_runner->setProcessEnvironment(env.toProcessEnvironment());
QTime executionTimer;
if (args.count()) {
- m_runner.start(runCmd, args);
+ m_runner->start(runCmd, args);
} else {
- m_runner.start(runCmd);
+ m_runner->start(runCmd);
}
- bool ok = m_runner.waitForStarted();
+ bool ok = m_runner->waitForStarted();
executionTimer.start();
if (ok) {
- while (m_runner.state() == QProcess::Running && executionTimer.elapsed() < timeout) {
+ while (m_runner->state() == QProcess::Running && executionTimer.elapsed() < timeout) {
+ if (m_currentFuture->isCanceled()) {
+ m_runner->kill();
+ m_runner->waitForFinished();
+ TestResultsPane::instance()->addTestResult(
+ TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
+ QObject::tr("*** Test Run canceled by user ***")));
+ }
qApp->processEvents();
}
}
if (ok && executionTimer.elapsed() < timeout) {
- return m_runner.exitCode() == 0;
+ return m_runner->exitCode() == 0;
} else {
return false;
}
}
+void performTestRun(QFutureInterface<void> &future, const QList<TestConfiguration *> selectedTests)
+{
+ int testCaseCount = 0;
+ foreach (const TestConfiguration *config, selectedTests)
+ testCaseCount += config->testCaseCount();
+
+ m_currentFuture = &future;
+ m_runner = new QProcess;
+ m_runner->setReadChannelMode(QProcess::MergedChannels);
+ m_runner->setReadChannel(QProcess::StandardOutput);
+
+ QObject::connect(m_runner, &QProcess::readyReadStandardOutput, &processOutput);
+
+ future.setProgressRange(0, testCaseCount);
+ future.setProgressValue(0);
+
+ foreach (const TestConfiguration *tc, selectedTests) {
+ if (future.isCanceled())
+ break;
+ QString cmd = tc->targetFile();
+ QString workDir = tc->workingDirectory();
+ QStringList args;
+ Utils::Environment env = tc->environment();
+
+ args << QLatin1String("-xml");
+ if (tc->testCases().count())
+ args << tc->testCases();
+
+ performExec(cmd, args, workDir, env);
+ }
+ future.setProgressValue(testCaseCount);
+
+ delete m_runner;
+ m_runner = 0;
+ m_currentFuture = 0;
+}
+
+void TestRunner::runTests()
+{
+ if (m_selectedTests.empty()) {
+ TestResultsPane::instance()->addTestResult(
+ TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
+ tr("*** No tests selected - canceling Test Run ***")));
+ return;
+ }
+
+ ProjectExplorer::Project *project = m_selectedTests.at(0)->project();
+
+ if (!project) // add a warning or info to output? possible at all?
+ return;
+
+ ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
+ ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings();
+ if (pes.buildBeforeDeploy) {
+ if (!project->hasActiveBuildSettings()) {
+ TestResultsPane::instance()->addTestResult(
+ TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
+ tr("*** Project is not configured - canceling Test Run ***")));
+ return;
+ }
+ buildProject(project);
+ while (m_building) {
+ qApp->processEvents();
+ }
+
+ if (!m_buildSucceeded) {
+ TestResultsPane::instance()->addTestResult(
+ TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL,
+ tr("*** Build failed - canceling Test Run ***")));
+ return;
+ }
+ }
+
+ // clear old log and output pane
+ TestResultsPane::instance()->clearContents();
+
+ emit testRunStarted();
+ QFuture<void> future = QtConcurrent::run(&performTestRun , m_selectedTests);
+ Core::FutureProgress *progress = Core::ProgressManager::addTask(future, tr("Running Tests"),
+ Autotest::Constants::TASK_INDEX);
+ connect(progress, &Core::FutureProgress::finished,
+ TestRunner::instance(), &TestRunner::testRunFinished);
+}
+
+void TestRunner::buildProject(ProjectExplorer::Project *project)
+{
+ m_building = true;
+ m_buildSucceeded = false;
+ ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
+ ProjectExplorer::BuildManager::instance());
+ ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance();
+ pep->buildProject(project);
+ connect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
+ this, &TestRunner::buildFinished);
+}
+
+void TestRunner::buildFinished(bool success)
+{
+ ProjectExplorer::BuildManager *mgr = static_cast<ProjectExplorer::BuildManager *>(
+ ProjectExplorer::BuildManager::instance());
+ disconnect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished,
+ this, &TestRunner::buildFinished);
+ m_building = false;
+ m_buildSucceeded = success;
+}
+
+void TestRunner::stopTestRun()
+{
+ if (m_runner && m_runner->state() != QProcess::NotRunning && m_currentFuture)
+ m_currentFuture->cancel();
+}
+
} // namespace Internal
} // namespace Autotest
diff --git a/plugins/autotest/testrunner.h b/plugins/autotest/testrunner.h
index 6ed530d05c..1a725eada7 100644
--- a/plugins/autotest/testrunner.h
+++ b/plugins/autotest/testrunner.h
@@ -51,23 +51,16 @@ public slots:
void stopTestRun();
private slots:
- void processOutput();
- void onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus);
void buildProject(ProjectExplorer::Project *project);
void buildFinished(bool success);
private:
explicit TestRunner(QObject *parent = 0);
- bool exec(const QString &cmd, const QStringList &args, const QString &workingDir = QString(),
- const Utils::Environment &env = Utils::Environment(), int timeout = 60000);
- QProcess m_runner;
QList<TestConfiguration *> m_selectedTests;
bool m_building;
bool m_buildSucceeded;
- QStringList m_xmlLog; // holds complete xml log of the last test run
-
};
} // namespace Internal