forked from qt-creator/qt-creator
AutoTest: add support for Catch framework
This adds a basic test and test output parser for the Catch2 test framework. Task-number: QTCREATORBUG-19740 Done-with: Christian Stenger <christian.stenger@qt.io> Change-Id: Ic3322905ab82f414d2a26c325e130142233f72aa Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
committed by
Christian Stenger
parent
13daf11d03
commit
de38285bc3
@@ -21,6 +21,10 @@ add_qtc_plugin(AutoTest
|
||||
boost/boosttestsettings.cpp boost/boosttestsettings.h
|
||||
boost/boosttestsettingspage.cpp boost/boosttestsettingspage.h boost/boosttestsettingspage.ui
|
||||
boost/boosttesttreeitem.cpp boost/boosttesttreeitem.h
|
||||
catch/catchconfiguration.h catch/catchconfiguration.cpp
|
||||
catch/catchframework.h catch/catchframework.cpp catch/catchoutputreader.h
|
||||
catch/catchoutputreader.cpp catch/catchresult.h catch/catchresult.cpp catch/catchtestparser.h
|
||||
catch/catchtestparser.cpp catch/catchtreeitem.h catch/catchtreeitem.cpp
|
||||
gtest/gtest_utils.cpp gtest/gtest_utils.h
|
||||
gtest/gtestconfiguration.cpp gtest/gtestconfiguration.h
|
||||
gtest/gtestconstants.h
|
||||
|
@@ -27,6 +27,12 @@ SOURCES += \
|
||||
testtreeitemdelegate.cpp \
|
||||
testtreemodel.cpp \
|
||||
testtreeview.cpp \
|
||||
catch/catchconfiguration.cpp \
|
||||
catch/catchframework.cpp \
|
||||
catch/catchoutputreader.cpp \
|
||||
catch/catchresult.cpp \
|
||||
catch/catchtestparser.cpp \
|
||||
catch/catchtreeitem.cpp \
|
||||
gtest/gtestconfiguration.cpp \
|
||||
gtest/gtestparser.cpp \
|
||||
gtest/gtesttreeitem.cpp \
|
||||
@@ -91,6 +97,12 @@ HEADERS += \
|
||||
testtreeitemdelegate.h \
|
||||
testtreemodel.h \
|
||||
testtreeview.h \
|
||||
catch/catchconfiguration.h \
|
||||
catch/catchframework.h \
|
||||
catch/catchoutputreader.h \
|
||||
catch/catchresult.h \
|
||||
catch/catchtestparser.h \
|
||||
catch/catchtreeitem.h \
|
||||
gtest/gtestconfiguration.h \
|
||||
gtest/gtestparser.h \
|
||||
gtest/gtesttreeitem.h \
|
||||
|
@@ -111,6 +111,13 @@ QtcPlugin {
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
name: "Catch framework files"
|
||||
files: [
|
||||
"catch/*"
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
name: "Test sources"
|
||||
condition: qtc.testsEnabled
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include "quick/quicktestframework.h"
|
||||
#include "gtest/gtestframework.h"
|
||||
#include "boost/boosttestframework.h"
|
||||
#include "catch/catchframework.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
@@ -133,6 +134,7 @@ AutotestPluginPrivate::AutotestPluginPrivate()
|
||||
m_frameworkManager.registerTestFramework(new QuickTestFramework);
|
||||
m_frameworkManager.registerTestFramework(new GTestFramework);
|
||||
m_frameworkManager.registerTestFramework(new BoostTestFramework);
|
||||
m_frameworkManager.registerTestFramework(new CatchFramework);
|
||||
|
||||
m_frameworkManager.synchronizeSettings(ICore::settings());
|
||||
m_navigationWidgetFactory = new TestNavigationWidgetFactory;
|
||||
|
55
src/plugins/autotest/catch/catchconfiguration.cpp
Normal file
55
src/plugins/autotest/catch/catchconfiguration.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchconfiguration.h"
|
||||
#include "catchoutputreader.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestOutputReader *CatchConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi, QProcess *app) const
|
||||
{
|
||||
return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
|
||||
}
|
||||
|
||||
QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) const
|
||||
{
|
||||
Q_UNUSED(omitted)
|
||||
|
||||
QStringList arguments;
|
||||
arguments << "--reporter" << "xml";
|
||||
|
||||
if (testCaseCount())
|
||||
arguments << "\"" + testCases().join("\",\"") + "\"";
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
Utils::Environment CatchConfiguration::filteredEnvironment(const Utils::Environment &original) const
|
||||
{
|
||||
return original;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
43
src/plugins/autotest/catch/catchconfiguration.h
Normal file
43
src/plugins/autotest/catch/catchconfiguration.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../testconfiguration.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchConfiguration : public DebuggableTestConfiguration
|
||||
{
|
||||
public:
|
||||
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
|
||||
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
|
||||
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
56
src/plugins/autotest/catch/catchframework.cpp
Normal file
56
src/plugins/autotest/catch/catchframework.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchframework.h"
|
||||
|
||||
#include "catchtestparser.h"
|
||||
#include "catchtreeitem.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
const char *CatchFramework::name() const
|
||||
{
|
||||
return "Catch";
|
||||
}
|
||||
|
||||
unsigned CatchFramework::priority() const
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
ITestParser *CatchFramework::createTestParser()
|
||||
{
|
||||
return new CatchTestParser(this);
|
||||
}
|
||||
|
||||
TestTreeItem *CatchFramework::createRootNode()
|
||||
{
|
||||
return new CatchTreeItem(this,
|
||||
QCoreApplication::translate("CatchFramework", "Catch Test"),
|
||||
QString(), TestTreeItem::Root);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
46
src/plugins/autotest/catch/catchframework.h
Normal file
46
src/plugins/autotest/catch/catchframework.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../itestframework.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchFramework : public ITestFramework
|
||||
{
|
||||
public:
|
||||
CatchFramework() : ITestFramework(true) {}
|
||||
|
||||
const char *name() const override;
|
||||
unsigned priority() const override;
|
||||
|
||||
protected:
|
||||
ITestParser *createTestParser() override;
|
||||
TestTreeItem *createRootNode() override;
|
||||
};
|
||||
|
||||
} // namepsace Internal
|
||||
} // namespace Autotest
|
272
src/plugins/autotest/catch/catchoutputreader.cpp
Normal file
272
src/plugins/autotest/catch/catchoutputreader.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchoutputreader.h"
|
||||
#include "catchresult.h"
|
||||
|
||||
#include "../testtreeitem.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
namespace CatchXml {
|
||||
const char GroupElement[] = "Group";
|
||||
const char TestCaseElement[] = "TestCase";
|
||||
const char SectionElement[] = "Section";
|
||||
const char ExpressionElement[] = "Expression";
|
||||
const char ExpandedElement[] = "Expanded";
|
||||
const char SectionResultElement[] = "OverallResults";
|
||||
const char TestCaseResultElement[] = "OverallResult";
|
||||
}
|
||||
|
||||
CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
|
||||
QProcess *testApplication, const QString &buildDirectory,
|
||||
const QString &projectFile)
|
||||
: TestOutputReader (futureInterface, testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
{
|
||||
}
|
||||
|
||||
void CatchOutputReader::processOutputLine(const QByteArray &outputLineWithNewLine)
|
||||
{
|
||||
if (outputLineWithNewLine.trimmed().isEmpty())
|
||||
return;
|
||||
|
||||
m_xmlReader.addData(QString::fromUtf8(outputLineWithNewLine));
|
||||
while (!m_xmlReader.atEnd()) {
|
||||
QXmlStreamReader::TokenType token = m_xmlReader.readNext();
|
||||
|
||||
switch (token) {
|
||||
case QXmlStreamReader::StartDocument:
|
||||
break;
|
||||
case QXmlStreamReader::EndDocument:
|
||||
m_xmlReader.clear();
|
||||
break;
|
||||
case QXmlStreamReader::StartElement: {
|
||||
m_currentTagName = m_xmlReader.name().toString();
|
||||
|
||||
if (m_currentTagName == CatchXml::GroupElement) {
|
||||
testOutputNodeStarted(GroupNode);
|
||||
} else if (m_currentTagName == CatchXml::TestCaseElement) {
|
||||
m_reportedResult = false;
|
||||
testOutputNodeStarted(TestCaseNode);
|
||||
recordTestInformation(m_xmlReader.attributes());
|
||||
sendResult(ResultType::TestStart);
|
||||
} else if (m_currentTagName == CatchXml::SectionElement) {
|
||||
testOutputNodeStarted(SectionNode);
|
||||
recordTestInformation(m_xmlReader.attributes());
|
||||
sendResult(ResultType::TestStart);
|
||||
} else if (m_currentTagName == CatchXml::TestCaseResultElement) {
|
||||
if (m_currentTestNode == OverallNode || m_currentTestNode == GroupNode)
|
||||
continue;
|
||||
if (m_reportedResult)
|
||||
continue;
|
||||
if (m_xmlReader.attributes().value("success").toString() == QStringLiteral("true"))
|
||||
sendResult(ResultType::Pass);
|
||||
else if (m_shouldFail)
|
||||
sendResult(ResultType::UnexpectedPass);
|
||||
} else if (m_currentTagName == CatchXml::SectionResultElement) {
|
||||
const QXmlStreamAttributes attributes = m_xmlReader.attributes();
|
||||
if (m_currentTestNode == OverallNode) { // the final results for the executable
|
||||
int passes = attributes.value("successes").toInt();
|
||||
int fails = attributes.value("failures").toInt();
|
||||
int xfails = attributes.value("expectedFailures").toInt();
|
||||
m_summary[ResultType::Pass] = passes - m_xpassCount;
|
||||
m_summary[ResultType::Fail] = fails;
|
||||
if (xfails)
|
||||
m_summary[ResultType::ExpectedFail] = xfails;
|
||||
if (m_xpassCount)
|
||||
m_summary[ResultType::UnexpectedPass] = m_xpassCount;
|
||||
}
|
||||
if (m_currentTestNode == OverallNode || m_currentTestNode == GroupNode)
|
||||
continue;
|
||||
if (attributes.value("failures").toInt() == 0)
|
||||
if (!m_reportedSectionResult)
|
||||
sendResult(ResultType::Pass);
|
||||
} else if (m_currentTagName == CatchXml::ExpressionElement) {
|
||||
recordTestInformation(m_xmlReader.attributes());
|
||||
if (m_xmlReader.attributes().value("success").toString() == QStringLiteral("true"))
|
||||
m_currentResult = m_shouldFail ? ResultType::UnexpectedPass : ResultType::Pass;
|
||||
else
|
||||
m_currentResult = m_mayFail || m_shouldFail ? ResultType::ExpectedFail : ResultType::Fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::Characters: {
|
||||
const QStringRef text = m_xmlReader.text();
|
||||
if (m_currentTagName == CatchXml::ExpandedElement) {
|
||||
m_currentExpression.append(text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::EndElement: {
|
||||
const QStringRef currentTag = m_xmlReader.name();
|
||||
|
||||
if (currentTag == CatchXml::SectionElement) {
|
||||
sendResult(ResultType::TestEnd);
|
||||
testOutputNodeFinished(SectionNode);
|
||||
} else if (currentTag == CatchXml::TestCaseElement) {
|
||||
sendResult(ResultType::TestEnd);
|
||||
testOutputNodeFinished(TestCaseNode);
|
||||
} else if (currentTag == CatchXml::GroupElement) {
|
||||
testOutputNodeFinished(GroupNode);
|
||||
} else if (currentTag == CatchXml::ExpressionElement) {
|
||||
sendResult(m_currentResult);
|
||||
m_currentExpression.clear();
|
||||
m_testCaseInfo.pop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestResultPtr CatchOutputReader::createDefaultResult() const
|
||||
{
|
||||
CatchResult *result = nullptr;
|
||||
if (m_testCaseInfo.size() > 0) {
|
||||
result = new CatchResult(id(), m_testCaseInfo.first().name);
|
||||
result->setDescription(m_testCaseInfo.last().name);
|
||||
result->setLine(m_testCaseInfo.last().line);
|
||||
const QString &relativePathFromBuildDir = m_testCaseInfo.last().filename;
|
||||
if (!relativePathFromBuildDir.isEmpty()) {
|
||||
const QFileInfo fileInfo(m_buildDir + '/' + relativePathFromBuildDir);
|
||||
result->setFileName(fileInfo.canonicalFilePath());
|
||||
}
|
||||
} else {
|
||||
result = new CatchResult(id(), QString());
|
||||
}
|
||||
result->setSectionDepth(m_sectionDepth);
|
||||
|
||||
return TestResultPtr(result);
|
||||
}
|
||||
|
||||
void CatchOutputReader::recordTestInformation(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QString name;
|
||||
if (attributes.hasAttribute("name")) // successful expressions do not have one
|
||||
name = attributes.value("name").toString();
|
||||
else if (!m_testCaseInfo.isEmpty())
|
||||
name = m_testCaseInfo.top().name;
|
||||
|
||||
m_testCaseInfo.append(TestOutputNode{
|
||||
name,
|
||||
attributes.value("filename").toString(),
|
||||
attributes.value("line").toInt()
|
||||
});
|
||||
if (attributes.hasAttribute("tags")) {
|
||||
const QString tags = attributes.value("tags").toString();
|
||||
m_mayFail = tags.contains("[!mayfail]");
|
||||
m_shouldFail = tags.contains("[!shouldfail]");
|
||||
}
|
||||
}
|
||||
|
||||
void CatchOutputReader::sendResult(const ResultType result)
|
||||
{
|
||||
TestResultPtr catchResult = createDefaultResult();
|
||||
catchResult->setResult(result);
|
||||
|
||||
if (result == ResultType::TestStart && m_testCaseInfo.size() > 0) {
|
||||
catchResult->setDescription(tr("Executing %1 \"%2\"").arg(testOutputNodeToString().toLower())
|
||||
.arg(catchResult->description()));
|
||||
} else if (result == ResultType::Pass || result == ResultType::UnexpectedPass) {
|
||||
if (result == ResultType::UnexpectedPass)
|
||||
++m_xpassCount;
|
||||
|
||||
if (m_currentExpression.isEmpty()) {
|
||||
catchResult->setDescription(tr("%1 \"%2\" passed").arg(testOutputNodeToString())
|
||||
.arg(catchResult->description()));
|
||||
} else {
|
||||
catchResult->setDescription(tr("Expression passed")
|
||||
.append('\n').append(m_currentExpression));
|
||||
}
|
||||
m_reportedSectionResult = true;
|
||||
m_reportedResult = true;
|
||||
} else if (result == ResultType::Fail || result == ResultType::ExpectedFail) {
|
||||
catchResult->setDescription(tr("Expression failed: %1").arg(m_currentExpression.trimmed()));
|
||||
if (!m_reportedSectionResult)
|
||||
m_reportedSectionResult = true;
|
||||
m_reportedResult = true;
|
||||
} else if (result == ResultType::TestEnd) {
|
||||
catchResult->setDescription(tr("Finished executing %1 \"%2\"").arg(testOutputNodeToString().toLower())
|
||||
.arg(catchResult->description()));
|
||||
}
|
||||
|
||||
reportResult(catchResult);
|
||||
}
|
||||
|
||||
void CatchOutputReader::testOutputNodeStarted(CatchOutputReader::TestOutputNodeType type)
|
||||
{
|
||||
m_currentTestNode = type;
|
||||
if (type == SectionNode) {
|
||||
++m_sectionDepth;
|
||||
m_reportedSectionResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CatchOutputReader::testOutputNodeFinished(CatchOutputReader::TestOutputNodeType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GroupNode:
|
||||
m_currentTestNode = OverallNode;
|
||||
return;
|
||||
case TestCaseNode:
|
||||
m_currentTestNode = GroupNode;
|
||||
m_testCaseInfo.pop();
|
||||
return;
|
||||
case SectionNode:
|
||||
--m_sectionDepth;
|
||||
m_testCaseInfo.pop();
|
||||
m_currentTestNode = m_sectionDepth == 0 ? TestCaseNode : SectionNode;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString CatchOutputReader::testOutputNodeToString() const
|
||||
{
|
||||
switch (m_currentTestNode) {
|
||||
case OverallNode:
|
||||
return QStringLiteral("Overall");
|
||||
case GroupNode:
|
||||
return QStringLiteral("Group");
|
||||
case TestCaseNode:
|
||||
return QStringLiteral("Test case");
|
||||
case SectionNode:
|
||||
return QStringLiteral("Section");
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
87
src/plugins/autotest/catch/catchoutputreader.h
Normal file
87
src/plugins/autotest/catch/catchoutputreader.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../testoutputreader.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QStack>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchOutputReader : public TestOutputReader
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::CatchOutputReader)
|
||||
|
||||
public:
|
||||
CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
|
||||
QProcess *testApplication, const QString &buildDirectory,
|
||||
const QString &projectFile);
|
||||
|
||||
protected:
|
||||
void processOutputLine(const QByteArray &outputLineWithNewLine) override;
|
||||
TestResultPtr createDefaultResult() const override;
|
||||
|
||||
private:
|
||||
enum TestOutputNodeType {
|
||||
OverallNode,
|
||||
GroupNode,
|
||||
TestCaseNode,
|
||||
SectionNode
|
||||
} m_currentTestNode = OverallNode;
|
||||
|
||||
struct TestOutputNode {
|
||||
QString name;
|
||||
QString filename;
|
||||
int line;
|
||||
};
|
||||
|
||||
void recordTestInformation(const QXmlStreamAttributes &attributes);
|
||||
void sendResult(const ResultType result);
|
||||
|
||||
void testOutputNodeStarted(TestOutputNodeType type);
|
||||
void testOutputNodeFinished(TestOutputNodeType type);
|
||||
|
||||
QString testOutputNodeToString() const;
|
||||
|
||||
QStack<TestOutputNode> m_testCaseInfo;
|
||||
int m_sectionDepth = 0;
|
||||
|
||||
QString m_projectFile;
|
||||
QString m_currentTagName;
|
||||
QString m_currentExpression;
|
||||
QXmlStreamReader m_xmlReader;
|
||||
ResultType m_currentResult = ResultType::Invalid;
|
||||
int m_xpassCount = 0;
|
||||
bool m_mayFail = false;
|
||||
bool m_shouldFail = false;
|
||||
bool m_reportedResult = false;
|
||||
bool m_reportedSectionResult = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
61
src/plugins/autotest/catch/catchresult.cpp
Normal file
61
src/plugins/autotest/catch/catchresult.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchresult.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
CatchResult::CatchResult(const QString &id, const QString &name)
|
||||
: TestResult(id, name)
|
||||
{
|
||||
}
|
||||
|
||||
bool CatchResult::isDirectParentOf(const TestResult *other, bool *needsIntermediate) const
|
||||
{
|
||||
if (!TestResult::isDirectParentOf(other, needsIntermediate))
|
||||
return false;
|
||||
const CatchResult *catchOther = static_cast<const CatchResult *>(other);
|
||||
|
||||
if (result() != ResultType::TestStart)
|
||||
return false;
|
||||
|
||||
if (catchOther->result() == ResultType::TestStart) {
|
||||
if (fileName() != catchOther->fileName())
|
||||
return false;
|
||||
|
||||
return sectionDepth() + 1 == catchOther->sectionDepth();
|
||||
}
|
||||
|
||||
if (sectionDepth() <= catchOther->sectionDepth() && catchOther->result() == ResultType::Pass)
|
||||
return true;
|
||||
|
||||
if (fileName() != catchOther->fileName() || sectionDepth() > catchOther->sectionDepth())
|
||||
return false;
|
||||
|
||||
return name() == catchOther->name();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
47
src/plugins/autotest/catch/catchresult.h
Normal file
47
src/plugins/autotest/catch/catchresult.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../testresult.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchResult : public TestResult
|
||||
{
|
||||
public:
|
||||
CatchResult(const QString &id, const QString &name);
|
||||
|
||||
bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const override;
|
||||
|
||||
void setSectionDepth(int depth) { m_sectionDepth = depth; }
|
||||
int sectionDepth() const { return m_sectionDepth; }
|
||||
|
||||
private:
|
||||
int m_sectionDepth = 0;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
288
src/plugins/autotest/catch/catchtestparser.cpp
Normal file
288
src/plugins/autotest/catch/catchtestparser.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchtestparser.h"
|
||||
|
||||
#include "catchtreeitem.h"
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <cpptools/projectpart.h>
|
||||
#include <cplusplus/LookupContext.h>
|
||||
#include <cplusplus/Overview.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct CatchTestCaseSpec
|
||||
{
|
||||
QString name;
|
||||
QStringList tags;
|
||||
};
|
||||
|
||||
static bool isCatchTestCaseMacro(const QString ¯oName)
|
||||
{
|
||||
const QStringList validTestCaseMacros = {
|
||||
QStringLiteral("TEST_CASE"), QStringLiteral("SCENARIO")
|
||||
};
|
||||
return validTestCaseMacros.contains(macroName);
|
||||
}
|
||||
|
||||
static bool isCatchMacro(const QString ¯oName)
|
||||
{
|
||||
const QStringList validSectionMacros = {
|
||||
QStringLiteral("SECTION"), QStringLiteral("WHEN")
|
||||
};
|
||||
return isCatchTestCaseMacro(macroName) || validSectionMacros.contains(macroName);
|
||||
}
|
||||
|
||||
static inline QString stringLiteralToQString(const CPlusPlus::StringLiteral *literal)
|
||||
{
|
||||
return QString::fromLatin1(literal->chars(), int(literal->size()));
|
||||
}
|
||||
|
||||
static QStringList parseTags(const QString &tagsString)
|
||||
{
|
||||
QStringList tagsList;
|
||||
|
||||
const QRegularExpression tagRegEx("\\[(.*?)\\]",QRegularExpression::CaseInsensitiveOption);
|
||||
int pos = 0;
|
||||
QRegularExpressionMatch it = tagRegEx.match(tagsString, pos);
|
||||
while (it.hasMatch()) {
|
||||
tagsList.append(it.captured(1));
|
||||
pos += it.capturedLength();
|
||||
it = tagRegEx.match(tagsString, pos);
|
||||
}
|
||||
return tagsList;
|
||||
}
|
||||
|
||||
class CatchTestVisitor : public CPlusPlus::ASTVisitor
|
||||
{
|
||||
public:
|
||||
explicit CatchTestVisitor(CPlusPlus::Document::Ptr doc)
|
||||
: CPlusPlus::ASTVisitor(doc->translationUnit()) , m_document(doc) {}
|
||||
bool visit(CPlusPlus::FunctionDefinitionAST *ast) override;
|
||||
|
||||
QMap<CatchTestCaseSpec, TestCodeLocationAndType> testFunctions() const { return m_testFunctions; }
|
||||
|
||||
private:
|
||||
bool isValidTestCase(unsigned int macroTokenIndex, QString &testCaseName, QStringList &tags);
|
||||
|
||||
CPlusPlus::Document::Ptr m_document;
|
||||
CPlusPlus::Overview m_overview;
|
||||
QMap<CatchTestCaseSpec, TestCodeLocationAndType> m_testFunctions;
|
||||
};
|
||||
|
||||
bool CatchTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast)
|
||||
{
|
||||
if (!ast || !ast->declarator || !ast->declarator->core_declarator)
|
||||
return false;
|
||||
|
||||
CPlusPlus::DeclaratorIdAST *id = ast->declarator->core_declarator->asDeclaratorId();
|
||||
if (!id || !id->name)
|
||||
return false;
|
||||
|
||||
const QString prettyName = m_overview.prettyName(id->name->name);
|
||||
if (!isCatchTestCaseMacro(prettyName))
|
||||
return false;
|
||||
|
||||
QString testName;
|
||||
QStringList tags;
|
||||
if (!isValidTestCase(ast->firstToken(), testName, tags))
|
||||
return false;
|
||||
|
||||
CatchTestCaseSpec spec;
|
||||
spec.name = testName;
|
||||
if (prettyName == "SCENARIO")
|
||||
spec.name.prepend("Scenario: "); // TODO maybe better as a flag?
|
||||
|
||||
spec.tags = tags;
|
||||
|
||||
TestCodeLocationAndType location;
|
||||
location.m_type = TestTreeItem::TestCase;
|
||||
location.m_name = m_document->fileName();
|
||||
getTokenStartPosition(ast->firstToken(), &location.m_line, &location.m_column);
|
||||
|
||||
m_testFunctions.insert(spec, location);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CatchTestVisitor::isValidTestCase(unsigned int macroTokenIndex, QString &testCaseName, QStringList &tags)
|
||||
{
|
||||
QTC_ASSERT(testCaseName.isEmpty(), return false);
|
||||
QTC_ASSERT(tags.isEmpty(), return false);
|
||||
|
||||
unsigned int end = tokenCount();
|
||||
++macroTokenIndex;
|
||||
|
||||
enum ParseMode { TestCaseMode, TagsMode, Ignored } mode = TestCaseMode;
|
||||
QString captured;
|
||||
while (macroTokenIndex < end) {
|
||||
CPlusPlus::Token token = tokenAt(macroTokenIndex);
|
||||
if (token.kind() == CPlusPlus::T_RPAREN) {
|
||||
if (mode == TagsMode)
|
||||
tags = parseTags(captured);
|
||||
else
|
||||
testCaseName = captured;
|
||||
break;
|
||||
} else if (token.kind() == CPlusPlus::T_COMMA) {
|
||||
if (mode == TestCaseMode) {
|
||||
mode = TagsMode;
|
||||
testCaseName = captured;
|
||||
captured = QString();
|
||||
} else if (mode == TagsMode) {
|
||||
mode = Ignored;
|
||||
}
|
||||
} else if (token.kind() == CPlusPlus::T_STRING_LITERAL) {
|
||||
if (mode != Ignored)
|
||||
captured += stringLiteralToQString(stringLiteral(macroTokenIndex));
|
||||
}
|
||||
++macroTokenIndex;
|
||||
}
|
||||
|
||||
return !testCaseName.isEmpty();
|
||||
}
|
||||
|
||||
inline bool operator<(const CatchTestCaseSpec &spec1, const CatchTestCaseSpec &spec2)
|
||||
{
|
||||
if (spec1.name != spec2.name)
|
||||
return spec1.name < spec2.name;
|
||||
if (spec1.tags != spec2.tags)
|
||||
return spec1.tags < spec2.tags;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool includesCatchHeader(const CPlusPlus::Document::Ptr &doc,
|
||||
const CPlusPlus::Snapshot &snapshot)
|
||||
{
|
||||
static const QString catchHeader("catch.hpp");
|
||||
for (const CPlusPlus::Document::Include &inc : doc->resolvedIncludes()) {
|
||||
if (inc.resolvedFileName().endsWith(catchHeader))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const QString &include : snapshot.allIncludesForDocument(doc->fileName())) {
|
||||
if (include.endsWith(catchHeader))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasCatchNames(const CPlusPlus::Document::Ptr &document)
|
||||
{
|
||||
for (const CPlusPlus::Document::MacroUse ¯o : document->macroUses()) {
|
||||
if (!macro.isFunctionLike())
|
||||
continue;
|
||||
|
||||
if (isCatchMacro(QLatin1String(macro.macro().name()))) {
|
||||
const QVector<CPlusPlus::Document::Block> args = macro.arguments();
|
||||
if (args.size() < 1)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInterface,
|
||||
const CPlusPlus::Document::Ptr &doc,
|
||||
const CPlusPlus::Snapshot &snapshot,
|
||||
ITestFramework *framework)
|
||||
{
|
||||
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
||||
const QString &filePath = doc->fileName();
|
||||
const QByteArray &fileContent = CppParser::getFileContent(filePath);
|
||||
CPlusPlus::Document::Ptr document = snapshot.preprocessedDocument(fileContent, filePath);
|
||||
document->check();
|
||||
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
||||
CatchTestVisitor visitor(document);
|
||||
visitor.accept(ast);
|
||||
|
||||
QMap<CatchTestCaseSpec, TestCodeLocationAndType> result = visitor.testFunctions();
|
||||
QString proFile;
|
||||
const QList<CppTools::ProjectPart::Ptr> &ppList = modelManager->projectPart(filePath);
|
||||
if (ppList.size())
|
||||
proFile = ppList.first()->projectFile;
|
||||
else
|
||||
return false;
|
||||
|
||||
CatchParseResult *parseResult = new CatchParseResult(framework);
|
||||
parseResult->itemType = TestTreeItem::TestCase;
|
||||
parseResult->fileName = filePath;
|
||||
parseResult->name = filePath;
|
||||
parseResult->displayName = filePath;
|
||||
QList<CppTools::ProjectPart::Ptr> projectParts = modelManager->projectPart(doc->fileName());
|
||||
if (projectParts.isEmpty()) // happens if shutting down while parsing
|
||||
return false;
|
||||
parseResult->proFile = projectParts.first()->projectFile;
|
||||
|
||||
for (const auto &testSpec : result.keys()) {
|
||||
const TestCodeLocationAndType &testLocation = result.value(testSpec);
|
||||
CatchParseResult *testCase = new CatchParseResult(framework);
|
||||
testCase->fileName = filePath;
|
||||
testCase->name = testSpec.name;
|
||||
testCase->proFile = proFile;
|
||||
testCase->itemType = TestTreeItem::TestFunction;
|
||||
testCase->line = testLocation.m_line;
|
||||
testCase->column = testLocation.m_column;
|
||||
|
||||
parseResult->children.append(testCase);
|
||||
}
|
||||
|
||||
futureInterface.reportResult(TestParseResultPtr(parseResult));
|
||||
|
||||
return !result.keys().isEmpty();
|
||||
}
|
||||
|
||||
bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> futureInterface, const QString &fileName)
|
||||
{
|
||||
CPlusPlus::Document::Ptr doc = document(fileName);
|
||||
if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot) || !hasCatchNames(doc))
|
||||
return false;
|
||||
|
||||
return handleCatchDocument(futureInterface, doc, m_cppSnapshot, framework());
|
||||
}
|
||||
|
||||
TestTreeItem *CatchParseResult::createTestTreeItem() const
|
||||
{
|
||||
if (itemType == TestTreeItem::Root)
|
||||
return nullptr;
|
||||
|
||||
CatchTreeItem *item = new CatchTreeItem(framework, name, fileName, itemType);
|
||||
item->setProFile(proFile);
|
||||
item->setLine(line);
|
||||
item->setColumn(column);
|
||||
|
||||
for (const TestParseResult *testSet : children)
|
||||
item->appendChild(testSet->createTestTreeItem());
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
50
src/plugins/autotest/catch/catchtestparser.h
Normal file
50
src/plugins/autotest/catch/catchtestparser.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../itestparser.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchParseResult : public TestParseResult
|
||||
{
|
||||
public:
|
||||
explicit CatchParseResult(ITestFramework *framework)
|
||||
: TestParseResult(framework) {}
|
||||
TestTreeItem *createTestTreeItem() const override;
|
||||
};
|
||||
|
||||
class CatchTestParser : public CppParser
|
||||
{
|
||||
public:
|
||||
CatchTestParser(ITestFramework *framework)
|
||||
: CppParser(framework) {}
|
||||
bool processDocument(QFutureInterface<TestParseResultPtr> futureInterface,
|
||||
const QString &fileName) override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
275
src/plugins/autotest/catch/catchtreeitem.cpp
Normal file
275
src/plugins/autotest/catch/catchtreeitem.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "catchtreeitem.h"
|
||||
#include "catchtestparser.h"
|
||||
#include "catchconfiguration.h"
|
||||
#include "catchframework.h"
|
||||
|
||||
#include <projectexplorer/session.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
QVariant CatchTreeItem::data(int column, int role) const
|
||||
{
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (type() == Root)
|
||||
break;
|
||||
return name();
|
||||
case Qt::CheckStateRole:
|
||||
switch (type()) {
|
||||
case Root:
|
||||
case GroupNode:
|
||||
case TestCase:
|
||||
case TestFunction:
|
||||
return checked();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
return TestTreeItem::data(column, role);
|
||||
}
|
||||
|
||||
TestTreeItem *CatchTreeItem::copyWithoutChildren()
|
||||
{
|
||||
CatchTreeItem *copied = new CatchTreeItem(framework());
|
||||
copied->copyBasicDataFrom(this);
|
||||
return copied;
|
||||
}
|
||||
|
||||
TestTreeItem *CatchTreeItem::find(const TestParseResult *result)
|
||||
{
|
||||
QTC_ASSERT(result, return nullptr);
|
||||
|
||||
switch (type()) {
|
||||
case Root:
|
||||
if (result->framework->grouping()) {
|
||||
const QString path = QFileInfo(result->fileName).absolutePath();
|
||||
for (int row = 0; row < childCount(); ++row) {
|
||||
TestTreeItem *group = childAt(row);
|
||||
if (group->filePath() != path)
|
||||
continue;
|
||||
if (auto groupChild = group->findChildByFile(result->fileName))
|
||||
return groupChild;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return findChildByFile(result->fileName);
|
||||
case GroupNode:
|
||||
return findChildByFile(result->fileName);
|
||||
case TestCase:
|
||||
return findChildByNameAndFile(result->name, result->fileName);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TestTreeItem *CatchTreeItem::findChild(const TestTreeItem *other)
|
||||
{
|
||||
QTC_ASSERT(other, return nullptr);
|
||||
|
||||
switch (type()) {
|
||||
case Root:
|
||||
return findChildByFileAndType(other->filePath(), other->type());
|
||||
case GroupNode:
|
||||
return other->type() == TestCase ? findChildByFile(other->filePath()) : nullptr;
|
||||
case TestCase:
|
||||
return findChildByNameAndFile(other->name(), other->filePath());
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool CatchTreeItem::modify(const TestParseResult *result)
|
||||
{
|
||||
QTC_ASSERT(result, return false);
|
||||
|
||||
switch (type()) {
|
||||
case TestCase:
|
||||
case TestFunction:
|
||||
return modifyTestFunctionContent(result);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TestTreeItem *CatchTreeItem::createParentGroupNode() const
|
||||
{
|
||||
const QFileInfo fileInfo(filePath());
|
||||
const QFileInfo base(fileInfo.absolutePath());
|
||||
return new CatchTreeItem(framework(), base.baseName(), fileInfo.absolutePath(), TestTreeItem::GroupNode);
|
||||
}
|
||||
|
||||
bool CatchTreeItem::canProvideTestConfiguration() const
|
||||
{
|
||||
return type() == TestFunction;
|
||||
}
|
||||
|
||||
bool CatchTreeItem::canProvideDebugConfiguration() const
|
||||
{
|
||||
return canProvideTestConfiguration();
|
||||
}
|
||||
|
||||
TestConfiguration *CatchTreeItem::testConfiguration() const
|
||||
{
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
QTC_ASSERT(project, return nullptr);
|
||||
|
||||
if (type() != TestFunction)
|
||||
return nullptr;
|
||||
|
||||
CatchConfiguration *config = nullptr;
|
||||
config = new CatchConfiguration(framework());
|
||||
config->setTestCaseCount(childCount());
|
||||
config->setProjectFile(proFile());
|
||||
config->setProject(project);
|
||||
config->setTestCases(QStringList(name()));
|
||||
config->setInternalTargets(internalTargets());
|
||||
return config;
|
||||
}
|
||||
|
||||
TestConfiguration *CatchTreeItem::debugConfiguration() const
|
||||
{
|
||||
CatchConfiguration *config = static_cast<CatchConfiguration *>(testConfiguration());
|
||||
if (config)
|
||||
config->setRunMode(TestRunMode::Debug);
|
||||
return config;
|
||||
}
|
||||
|
||||
struct CatchTestCases
|
||||
{
|
||||
QStringList names;
|
||||
QSet<QString> internalTargets;
|
||||
};
|
||||
|
||||
static void collectTestInfo(const TestTreeItem *item,
|
||||
QHash<QString, CatchTestCases> &testCasesForProfile,
|
||||
bool ignoreCheckState)
|
||||
{
|
||||
QTC_ASSERT(item, return);
|
||||
const int childCount = item->childCount();
|
||||
if (item->type() == TestTreeItem::GroupNode) {
|
||||
item->forFirstLevelChildren([&testCasesForProfile, ignoreCheckState](TestTreeItem *it) {
|
||||
collectTestInfo(it, testCasesForProfile, ignoreCheckState);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
QTC_ASSERT(childCount != 0, return);
|
||||
QTC_ASSERT(item->type() == TestTreeItem::TestCase, return);
|
||||
if (ignoreCheckState || item->checked() == Qt::Checked) {
|
||||
const QString &projectFile = item->childAt(0)->proFile();
|
||||
item->forAllChildren([&testCasesForProfile, &projectFile](TestTreeItem *it) {
|
||||
testCasesForProfile[projectFile].names.append(it->name());
|
||||
});
|
||||
testCasesForProfile[projectFile].internalTargets.unite(item->internalTargets());
|
||||
} else if (item->checked() == Qt::PartiallyChecked) {
|
||||
item->forFirstLevelChildren([&testCasesForProfile](TestTreeItem *child) {
|
||||
QTC_ASSERT(child->type() == TestTreeItem::TestFunction, return);
|
||||
if (child->checked() == Qt::Checked) {
|
||||
testCasesForProfile[child->proFile()].names.append(child->name());
|
||||
testCasesForProfile[child->proFile()].internalTargets.unite(
|
||||
child->internalTargets());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> CatchTreeItem::getAllTestConfigurations() const
|
||||
{
|
||||
return getTestConfigurations(true);
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> CatchTreeItem::getSelectedTestConfigurations() const
|
||||
{
|
||||
return getTestConfigurations(false);
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Utils::FilePath &fileName) const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project || type() != Root)
|
||||
return result;
|
||||
|
||||
const QString filePath = fileName.toString();
|
||||
for (int row = 0, count = childCount(); row < count; ++row) {
|
||||
const TestTreeItem *item = childAt(row);
|
||||
QTC_ASSERT(item, continue);
|
||||
|
||||
if (childAt(row)->name() != filePath)
|
||||
continue;
|
||||
|
||||
CatchConfiguration *testConfig = nullptr;
|
||||
QStringList testCases;
|
||||
|
||||
item->forFirstLevelChildren([&testCases](TestTreeItem *child) {
|
||||
testCases << child->name();
|
||||
});
|
||||
|
||||
testConfig = new CatchConfiguration(framework());
|
||||
testConfig->setTestCases(testCases);
|
||||
testConfig->setProjectFile(item->proFile());
|
||||
testConfig->setProject(ProjectExplorer::SessionManager::startupProject());
|
||||
testConfig->setInternalTargets(item->internalTargets());
|
||||
result << testConfig;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> CatchTreeItem::getTestConfigurations(bool ignoreCheckState) const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project || type() != Root)
|
||||
return result;
|
||||
|
||||
QHash<QString, CatchTestCases> testCasesForProfile;
|
||||
forFirstLevelChildren([&testCasesForProfile, ignoreCheckState](TestTreeItem *item) {
|
||||
collectTestInfo(item, testCasesForProfile, ignoreCheckState);
|
||||
});
|
||||
|
||||
for (auto it = testCasesForProfile.begin(), end = testCasesForProfile.end(); it != end; ++it) {
|
||||
for (const QString &target : qAsConst(it.value().internalTargets)) {
|
||||
CatchConfiguration *tc = new CatchConfiguration(framework());
|
||||
tc->setTestCases(it.value().names);
|
||||
if (ignoreCheckState)
|
||||
tc->setTestCaseCount(0);
|
||||
tc->setProjectFile(it.key());
|
||||
tc->setProject(project);
|
||||
tc->setInternalTarget(target);
|
||||
result << tc;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
60
src/plugins/autotest/catch/catchtreeitem.h
Normal file
60
src/plugins/autotest/catch/catchtreeitem.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Jochen Seemann
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../testtreeitem.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class CatchTreeItem : public TestTreeItem
|
||||
{
|
||||
public:
|
||||
explicit CatchTreeItem(ITestFramework *framework, const QString &name = QString(),
|
||||
const QString &filePath = QString(), Type type = Root)
|
||||
: TestTreeItem(framework, name, filePath, type) {}
|
||||
|
||||
QVariant data(int column, int role) const override;
|
||||
|
||||
TestTreeItem *copyWithoutChildren() override;
|
||||
TestTreeItem *find(const TestParseResult *result) override;
|
||||
TestTreeItem *findChild(const TestTreeItem *other) override;
|
||||
bool modify(const TestParseResult *result) override;
|
||||
TestTreeItem *createParentGroupNode() const override;
|
||||
|
||||
bool canProvideTestConfiguration() const override;
|
||||
bool canProvideDebugConfiguration() const override;
|
||||
TestConfiguration *testConfiguration() const override;
|
||||
TestConfiguration *debugConfiguration() const override;
|
||||
QList<TestConfiguration *> getAllTestConfigurations() const override;
|
||||
QList<TestConfiguration *> getSelectedTestConfigurations() const override;
|
||||
QList<TestConfiguration *> getTestConfigurationsForFile(const Utils::FilePath &fileName) const override;
|
||||
|
||||
private:
|
||||
QList<TestConfiguration *> getTestConfigurations(bool ignoreCheckState) const;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
Reference in New Issue
Block a user