Commit 650be0e4 authored by Christian Stenger's avatar Christian Stenger
Browse files

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: default avatarNiels Weber <niels.weber@theqtcompany.com>
parent db0ff4f9
......@@ -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 += \
......
......@@ -69,8 +69,8 @@ QtcCommercialPlugin {
"testtreeview.h",
"testvisitor.cpp",
"testvisitor.h",
"testxmloutputreader.cpp",
"testxmloutputreader.h",
"testoutputreader.cpp",
"testoutputreader.h",
]
Group {
......
......@@ -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)) {
......
......@@ -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);
basicProjectInformation(project, m_mainFilePath, &proFile, &displayName, &targetProject);
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
......@@ -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
......
......@@ -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
......@@ -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
......@@ -160,18 +160,20 @@ void TestResultModel::addTestResult(TestResult *testResult, bool autoExpand)
TestResultItem *newItem = new TestResultItem(testResult);
// FIXME this might be totally wrong... we need some more unique information!
for (int row = lastRow; row >= 0; --row) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
const TestResult *result = current->testResult();
if (result && result->className() == testResult->className()) {
current->appendChild(newItem);
if (autoExpand)
current->expand();
if (testResult->result() == Result::MessageTestCaseEnd) {
current->updateResult();
emit dataChanged(current->index(), current->index());
if (!testResult->className().isEmpty()) {
for (int row = lastRow; row >= 0; --row) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
const TestResult *result = current->testResult();
if (result && result->className() == testResult->className()) {
current->appendChild(newItem);
if (autoExpand)
current->expand();
if (testResult->result() == Result::MessageTestCaseEnd) {
current->updateResult();
emit dataChanged(current->index(), current->index());
}
return;
}
return;
}
}
// if we have a MessageCurrentTest present, add the new top level item before it
......
......@@ -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;
}
QStringList argumentList(QLatin1String("-xml"));
if (!metricsOption.isEmpty())
argumentList << metricsOption;
if (testConfiguration->testCases().count())
argumentList << testConfiguration->testCases();
testProcess.setArguments(argumentList);
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())
......
......@@ -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 {
......
......@@ -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;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment