Add capability to run gtest related tests

For now this only applies for 'Run All'. To be able to run only
selected tests we first have to introduce the check state for
gtest related items as well.

Change-Id: I196b56b7fe426f846f2be0df7e21458c2733cbd1
Reviewed-by: Niels Weber <niels.weber@theqtcompany.com>
This commit is contained in:
Christian Stenger
2015-12-09 09:46:33 +01:00
parent db0ff4f940
commit 650be0e496
11 changed files with 228 additions and 43 deletions

View File

@@ -27,7 +27,7 @@ SOURCES += \
testsettings.cpp \
testsettingspage.cpp \
testnavigationwidget.cpp \
testxmloutputreader.cpp
testoutputreader.cpp
HEADERS += \
testtreeview.h \
@@ -50,7 +50,7 @@ HEADERS += \
testsettings.h \
testsettingspage.h \
testnavigationwidget.h \
testxmloutputreader.h \
testoutputreader.h \
autotesticons.h
RESOURCES += \

View File

@@ -69,8 +69,8 @@ QtcCommercialPlugin {
"testtreeview.h",
"testvisitor.cpp",
"testvisitor.h",
"testxmloutputreader.cpp",
"testxmloutputreader.h",
"testoutputreader.cpp",
"testoutputreader.h",
]
Group {

View File

@@ -460,6 +460,7 @@ static TestTreeItem *constructTestTreeItem(const QString &fileName,
}
static TestTreeItem *constructGTestTreeItem(const QString &filePath, const QString &caseName,
const QString &proFile,
const TestCodeLocationList &testNames)
{
TestTreeItem *item = new TestTreeItem(caseName, QString(), TestTreeItem::GTestCase);
@@ -468,6 +469,7 @@ static TestTreeItem *constructGTestTreeItem(const QString &filePath, const QStri
locationAndType.m_type);
treeItemChild->setLine(locationAndType.m_line);
treeItemChild->setColumn(locationAndType.m_column);
treeItemChild->setMainFile(proFile);
item->appendChild(treeItemChild);
}
return item;
@@ -1030,7 +1032,7 @@ void TestCodeParser::updateGTests(const CPlusPlus::Document::Ptr &doc,
proFile = ppList.at(0)->projectFile;
foreach (const QString &testName, tests.keys()) {
TestTreeItem *item = constructGTestTreeItem(fileName, testName, tests.value(testName));
TestTreeItem *item = constructGTestTreeItem(fileName, testName, proFile, tests.value(testName));
TestInfo info(item->name(), item->getChildNames(), doc->revision(), doc->editorRevision());
info.setProfile(proFile);
foreach (const TestCodeLocationAndType &testSet, tests.value(testName)) {

View File

@@ -42,7 +42,8 @@ TestConfiguration::TestConfiguration(const QString &testClass, const QStringList
m_testCaseCount(testCaseCount),
m_unnamedOnly(false),
m_project(0),
m_guessedConfiguration(false)
m_guessedConfiguration(false),
m_type(Qt)
{
if (testCases.size() != 0)
m_testCaseCount = testCases.size();
@@ -71,6 +72,21 @@ void basicProjectInformation(Project *project, const QString &mainFilePath, QStr
}
}
void basicProjectInformation(Project *project, const QString &proFile, QString *displayName,
Project **targetProject)
{
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectInfo(project).projectParts();
foreach (const CppTools::ProjectPart::Ptr &part, projParts) {
if (part->projectFile == proFile) {
*displayName = part->displayName;
*targetProject = part->project;
return;
}
}
}
void extractEnvironmentInformation(LocalApplicationRunConfiguration *localRunConfiguration,
QString *workDir, Utils::Environment *env)
{
@@ -81,7 +97,7 @@ void extractEnvironmentInformation(LocalApplicationRunConfiguration *localRunCon
void TestConfiguration::completeTestInformation()
{
QTC_ASSERT(!m_mainFilePath.isEmpty(), return);
QTC_ASSERT(!m_mainFilePath.isEmpty() || !m_proFile.isEmpty(), return);
typedef LocalApplicationRunConfiguration LocalRunConfig;
@@ -92,7 +108,7 @@ void TestConfiguration::completeTestInformation()
QString targetFile;
QString targetName;
QString workDir;
QString proFile;
QString proFile = m_proFile;
QString displayName;
Project *targetProject = 0;
Utils::Environment env;
@@ -100,7 +116,10 @@ void TestConfiguration::completeTestInformation()
bool guessedRunConfiguration = false;
setProject(0);
if (m_proFile.isEmpty())
basicProjectInformation(project, m_mainFilePath, &proFile, &displayName, &targetProject);
else
basicProjectInformation(project, proFile, &displayName, &targetProject);
Target *target = project->activeTarget();
if (!target)
@@ -220,5 +239,10 @@ void TestConfiguration::setGuessedConfiguration(bool guessed)
m_guessedConfiguration = guessed;
}
void TestConfiguration::setTestType(TestType type)
{
m_type = type;
}
} // namespace Internal
} // namespace Autotest

View File

@@ -37,6 +37,11 @@ class TestConfiguration : public QObject
{
Q_OBJECT
public:
enum TestType {
Qt,
GTest
};
explicit TestConfiguration(const QString &testClass, const QStringList &testCases,
int testCaseCount = 0, QObject *parent = 0);
~TestConfiguration();
@@ -55,6 +60,7 @@ public:
void setProject(ProjectExplorer::Project *project);
void setUnnamedOnly(bool unnamedOnly);
void setGuessedConfiguration(bool guessed);
void setTestType(TestType type);
QString testClass() const { return m_testClass; }
QStringList testCases() const { return m_testCases; }
@@ -68,6 +74,7 @@ public:
ProjectExplorer::Project *project() const { return m_project; }
bool unnamedOnly() const { return m_unnamedOnly; }
bool guessedConfiguration() const { return m_guessedConfiguration; }
TestType testType() const { return m_type; }
private:
QString m_testClass;
@@ -83,6 +90,7 @@ private:
Utils::Environment m_environment;
ProjectExplorer::Project *m_project;
bool m_guessedConfiguration;
TestType m_type;
};
} // namespace Internal

View File

@@ -17,7 +17,7 @@
**
****************************************************************************/
#include "testxmloutputreader.h"
#include "testoutputreader.h"
#include "testresult.h"
#include <utils/hostosinfo.h>
@@ -28,6 +28,7 @@
#include <QProcess>
#include <QFileInfo>
#include <QDir>
#include <QXmlStreamReader>
namespace Autotest {
namespace Internal {
@@ -130,11 +131,10 @@ static QString constructBenchmarkInformation(const QString &metric, double value
.arg(iterations);
}
TestXmlOutputReader::TestXmlOutputReader(QProcess *testApplication)
:m_testApplication(testApplication)
TestOutputReader::TestOutputReader(QProcess *testApplication, OutputType type)
: m_testApplication(testApplication)
, m_type(type)
{
connect(m_testApplication, &QProcess::readyReadStandardOutput,
this, &TestXmlOutputReader::processOutput);
}
enum CDATAMode {
@@ -146,7 +146,7 @@ enum CDATAMode {
QTestVersion
};
void TestXmlOutputReader::processOutput()
void TestOutputReader::processOutput()
{
if (!m_testApplication || m_testApplication->state() != QProcess::Running)
return;
@@ -306,5 +306,110 @@ void TestXmlOutputReader::processOutput()
}
}
void TestOutputReader::processGTestOutput()
{
if (!m_testApplication || m_testApplication->state() != QProcess::Running)
return;
static QRegExp newTestStarts(QStringLiteral("^\\[-{10}\\] \\d+ tests? from (.*)$"));
static QRegExp testEnds(QStringLiteral("^\\[-{10}\\] \\d+ tests? from (.*) \\((.*)\\)$"));
static QRegExp newTestSetStarts(QStringLiteral("^\\[ RUN \\] (.*)$"));
static QRegExp testSetSuccess(QStringLiteral("^\\[ OK \\] (.*) \\((.*)\\)$"));
static QRegExp testSetFail(QStringLiteral("^\\\[ FAILED \\] (.*) \\((.*)\\)$"));
static QRegExp disabledTests(QStringLiteral("^ YOU HAVE (\\d+) DISABLED TESTS?$"));
static QString currentTestName;
static QString currentTestSet;
static QString description;
static QByteArray unprocessed;
while (m_testApplication->canReadLine())
unprocessed.append(m_testApplication->readLine());
int lineBreak;
while ((lineBreak = unprocessed.indexOf('\n')) != -1) {
const QString line = QLatin1String(unprocessed.left(lineBreak));
unprocessed.remove(0, lineBreak + 1);
if (line.isEmpty()) {
continue;
}
if (!line.startsWith(QLatin1Char('['))) {
description.append(line).append(QLatin1Char('\n'));
if (line.startsWith(QStringLiteral("Note:"))) {
auto testResult = new TestResult();
testResult->setResult(Result::MessageInternal);
testResult->setDescription(line);
testResultCreated(testResult);
description.clear();
} else if (disabledTests.exactMatch(line)) {
auto testResult = new TestResult();
testResult->setResult(Result::MessageInternal);
int disabled = disabledTests.cap(1).toInt();
testResult->setDescription(tr("You have %n disabled test(s).", 0, disabled));
testResultCreated(testResult);
description.clear();
}
continue;
}
if (testEnds.exactMatch(line)) {
auto testResult = new TestResult(currentTestName);
testResult->setTestCase(currentTestSet);
testResult->setResult(Result::MessageTestCaseEnd);
testResult->setDescription(tr("Test execution took %1").arg(testEnds.cap(2)));
testResultCreated(testResult);
currentTestName.clear();
currentTestSet.clear();
} else if (newTestStarts.exactMatch(line)) {
currentTestName = newTestStarts.cap(1);
auto testResult = new TestResult(currentTestName);
testResult->setResult(Result::MessageTestCaseStart);
testResult->setDescription(tr("Executing test case %1").arg(currentTestName));
testResultCreated(testResult);
} else if (newTestSetStarts.exactMatch(line)) {
currentTestSet = newTestSetStarts.cap(1);
auto testResult = new TestResult();
testResult->setResult(Result::MessageCurrentTest);
testResult->setDescription(tr("Entering test set %1").arg(currentTestSet));
testResultCreated(testResult);
} else if (testSetSuccess.exactMatch(line)) {
auto testResult = new TestResult(currentTestName);
testResult->setTestCase(currentTestSet);
testResult->setResult(Result::Pass);
testResultCreated(testResult);
testResult = new TestResult(currentTestName);
testResult->setTestCase(currentTestSet);
testResult->setResult(Result::MessageInternal);
testResult->setDescription(tr("Execution took %1.").arg(testSetSuccess.cap(2)));
testResultCreated(testResult);
emit increaseProgress();
} else if (testSetFail.exactMatch(line)) {
auto testResult = new TestResult(currentTestName);
testResult->setTestCase(currentTestSet);
testResult->setResult(Result::Fail);
description.chop(1);
testResult->setDescription(description);
int firstColon = description.indexOf(QLatin1Char(':'));
if (firstColon != -1) {
int secondColon = description.indexOf(QLatin1Char(':'), firstColon + 1);
QString file = constructSourceFilePath(m_testApplication->workingDirectory(),
description.left(firstColon),
m_testApplication->program());
QString line = description.mid(firstColon + 1, secondColon - firstColon - 1);
testResult->setFileName(file);
testResult->setLine(line.toInt());
}
testResultCreated(testResult);
description.clear();
testResult = new TestResult(currentTestName);
testResult->setTestCase(currentTestSet);
testResult->setResult(Result::MessageInternal);
testResult->setDescription(tr("Execution took %1.").arg(testSetFail.cap(2)));
testResultCreated(testResult);
emit increaseProgress();
}
}
}
} // namespace Internal
} // namespace Autotest

View File

@@ -24,7 +24,6 @@
#include <QObject>
#include <QString>
#include <QXmlStreamReader>
QT_BEGIN_NAMESPACE
class QProcess;
@@ -33,14 +32,20 @@ QT_END_NAMESPACE
namespace Autotest {
namespace Internal {
class TestXmlOutputReader : public QObject
class TestOutputReader : public QObject
{
Q_OBJECT
public:
TestXmlOutputReader(QProcess *testApplication);
enum OutputType {
Qt,
GTest
};
TestOutputReader(QProcess *testApplication, OutputType type = Qt);
public slots:
void processOutput();
void processGTestOutput();
signals:
void testResultCreated(TestResult *testResult);
@@ -48,9 +53,10 @@ signals:
private:
QProcess *m_testApplication; // not owned
OutputType m_type;
};
} // namespace Internal
} // namespace Autotest
#endif // TESTXMLOUTPUTREADER_H
#endif // TESTOUTPUTREADER_H

View File

@@ -160,6 +160,7 @@ void TestResultModel::addTestResult(TestResult *testResult, bool autoExpand)
TestResultItem *newItem = new TestResultItem(testResult);
// FIXME this might be totally wrong... we need some more unique information!
if (!testResult->className().isEmpty()) {
for (int row = lastRow; row >= 0; --row) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
const TestResult *result = current->testResult();
@@ -174,6 +175,7 @@ void TestResultModel::addTestResult(TestResult *testResult, bool autoExpand)
return;
}
}
}
// if we have a MessageCurrentTest present, add the new top level item before it
if (lastRow >= 0) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));

View File

@@ -22,7 +22,7 @@
#include "autotestplugin.h"
#include "testresultspane.h"
#include "testsettings.h"
#include "testxmloutputreader.h"
#include "testoutputreader.h"
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
@@ -36,6 +36,7 @@
#include <QFuture>
#include <QFutureInterface>
#include <QMetaObject>
#include <QTime>
namespace Autotest {
@@ -109,10 +110,14 @@ void performTestRun(QFutureInterface<void> &futureInterface,
const QString metricsOption, TestRunner* testRunner)
{
int testCaseCount = 0;
bool hasQtTests = false;
bool hasGTests = false;
foreach (TestConfiguration *config, selectedTests) {
config->completeTestInformation();
if (config->project()) {
testCaseCount += config->testCaseCount();
hasQtTests |= config->testType() == TestConfiguration::Qt;
hasGTests |= config->testType() == TestConfiguration::GTest;
} else {
emitTestResultCreated(new FaultyTestResult(Result::MessageWarn,
QObject::tr("Project is null for \"%1\". Removing from test run.\n"
@@ -127,18 +132,28 @@ void performTestRun(QFutureInterface<void> &futureInterface,
futureInterface.cancel(); // this kills the process if that is still in the running loop
});
TestXmlOutputReader xmlReader(&testProcess);
QObject::connect(&xmlReader, &TestXmlOutputReader::increaseProgress, [&] () {
TestOutputReader outputReader(&testProcess);
QObject::connect(&outputReader, &TestOutputReader::increaseProgress, [&] () {
futureInterface.setProgressValue(futureInterface.progressValue() + 1);
});
QObject::connect(&xmlReader, &TestXmlOutputReader::testResultCreated, &emitTestResultCreated);
QObject::connect(&testProcess, &QProcess::readyRead, &xmlReader, &TestXmlOutputReader::processOutput);
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);
TestConfiguration::TestType testType = testConfiguration->testType();
if (testType == TestConfiguration::Qt) {
connection = QObject::connect(&testProcess, &QProcess::readyRead, &outputReader,
&TestOutputReader::processOutput);
} else {
connection = QObject::connect(&testProcess, &QProcess::readyRead, &outputReader,
&TestOutputReader::processGTestOutput);
}
if (futureInterface.isCanceled())
break;
@@ -155,12 +170,14 @@ void performTestRun(QFutureInterface<void> &futureInterface,
continue;
}
if (testType == TestConfiguration::Qt) {
QStringList argumentList(QLatin1String("-xml"));
if (!metricsOption.isEmpty())
argumentList << metricsOption;
if (testConfiguration->testCases().count())
argumentList << testConfiguration->testCases();
testProcess.setArguments(argumentList);
}
testProcess.setWorkingDirectory(testConfiguration->workingDirectory());
if (Utils::HostOsInfo::isWindowsHost())

View File

@@ -86,7 +86,7 @@ private:
Type m_type;
unsigned m_line;
unsigned m_column;
QString m_mainFile;
QString m_mainFile; // main for Quick tests, project file for gtest
};
struct TestCodeLocationAndType {

View File

@@ -236,6 +236,27 @@ QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
result << tc;
}
foundMains.clear();
// get all Google Tests
for (int row = 0, count = m_googleTestRootItem->childCount(); row < count; ++row) {
const TestTreeItem *child = m_googleTestRootItem->childItem(row);
for (int childRow = 0, childCount = child->childCount(); childRow < childCount; ++childRow) {
const QString &proFilePath = child->childItem(childRow)->mainFile();
foundMains.insert(proFilePath, foundMains.contains(proFilePath)
? foundMains.value(proFilePath) + 1 : 1);
}
}
foreach (const QString &proFile, foundMains.keys()) {
TestConfiguration *tc = new TestConfiguration(QString(), QStringList(),
foundMains.value(proFile));
tc->setProFile(proFile);
tc->setProject(project);
tc->setTestType(TestConfiguration::GTest);
result << tc;
}
return result;
}