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 \ testsettings.cpp \
testsettingspage.cpp \ testsettingspage.cpp \
testnavigationwidget.cpp \ testnavigationwidget.cpp \
testxmloutputreader.cpp testoutputreader.cpp
HEADERS += \ HEADERS += \
testtreeview.h \ testtreeview.h \
@@ -50,7 +50,7 @@ HEADERS += \
testsettings.h \ testsettings.h \
testsettingspage.h \ testsettingspage.h \
testnavigationwidget.h \ testnavigationwidget.h \
testxmloutputreader.h \ testoutputreader.h \
autotesticons.h autotesticons.h
RESOURCES += \ RESOURCES += \

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@
** **
****************************************************************************/ ****************************************************************************/
#include "testxmloutputreader.h" #include "testoutputreader.h"
#include "testresult.h" #include "testresult.h"
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -28,6 +28,7 @@
#include <QProcess> #include <QProcess>
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QXmlStreamReader>
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
@@ -130,11 +131,10 @@ static QString constructBenchmarkInformation(const QString &metric, double value
.arg(iterations); .arg(iterations);
} }
TestXmlOutputReader::TestXmlOutputReader(QProcess *testApplication) TestOutputReader::TestOutputReader(QProcess *testApplication, OutputType type)
:m_testApplication(testApplication) : m_testApplication(testApplication)
, m_type(type)
{ {
connect(m_testApplication, &QProcess::readyReadStandardOutput,
this, &TestXmlOutputReader::processOutput);
} }
enum CDATAMode { enum CDATAMode {
@@ -146,7 +146,7 @@ enum CDATAMode {
QTestVersion QTestVersion
}; };
void TestXmlOutputReader::processOutput() void TestOutputReader::processOutput()
{ {
if (!m_testApplication || m_testApplication->state() != QProcess::Running) if (!m_testApplication || m_testApplication->state() != QProcess::Running)
return; 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 Internal
} // namespace Autotest } // namespace Autotest

View File

@@ -24,7 +24,6 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QXmlStreamReader>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QProcess; class QProcess;
@@ -33,14 +32,20 @@ QT_END_NAMESPACE
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
class TestXmlOutputReader : public QObject class TestOutputReader : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
TestXmlOutputReader(QProcess *testApplication); enum OutputType {
Qt,
GTest
};
TestOutputReader(QProcess *testApplication, OutputType type = Qt);
public slots: public slots:
void processOutput(); void processOutput();
void processGTestOutput();
signals: signals:
void testResultCreated(TestResult *testResult); void testResultCreated(TestResult *testResult);
@@ -48,9 +53,10 @@ signals:
private: private:
QProcess *m_testApplication; // not owned QProcess *m_testApplication; // not owned
OutputType m_type;
}; };
} // namespace Internal } // namespace Internal
} // namespace Autotest } // 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); TestResultItem *newItem = new TestResultItem(testResult);
// FIXME this might be totally wrong... we need some more unique information! // FIXME this might be totally wrong... we need some more unique information!
if (!testResult->className().isEmpty()) {
for (int row = lastRow; row >= 0; --row) { for (int row = lastRow; row >= 0; --row) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row)); TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
const TestResult *result = current->testResult(); const TestResult *result = current->testResult();
@@ -174,6 +175,7 @@ void TestResultModel::addTestResult(TestResult *testResult, bool autoExpand)
return; return;
} }
} }
}
// if we have a MessageCurrentTest present, add the new top level item before it // if we have a MessageCurrentTest present, add the new top level item before it
if (lastRow >= 0) { if (lastRow >= 0) {
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow)); TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));

View File

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

View File

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

View File

@@ -236,6 +236,27 @@ QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
result << tc; 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; return result;
} }