aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/autotest/catch
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@qt.io>2020-04-14 13:02:33 +0200
committerChristian Stenger <christian.stenger@qt.io>2020-04-17 06:53:26 +0000
commit96fe0aab8db2d9edbbf31b515598d70ca2c35b31 (patch)
treea0f206bf46749a622390b959324bc5278f16a67c /src/plugins/autotest/catch
parent2795292c74ff64d06b12eeed5c41bd107c3ce5b9 (diff)
AutoTest: Support missing Catch2 test case macros
Add support for macros defining parameterized test cases and for test cases using a fixture. Task-number: QTCREATORBUG-19740 Change-Id: I631009f309cb48d2657acb6e52911e052ff85c5b Reviewed-by: David Schulz <david.schulz@qt.io>
Diffstat (limited to 'src/plugins/autotest/catch')
-rw-r--r--src/plugins/autotest/catch/catchcodeparser.cpp115
-rw-r--r--src/plugins/autotest/catch/catchcodeparser.h8
-rw-r--r--src/plugins/autotest/catch/catchtestparser.cpp17
-rw-r--r--src/plugins/autotest/catch/catchtestparser.h2
-rw-r--r--src/plugins/autotest/catch/catchtreeitem.cpp28
-rw-r--r--src/plugins/autotest/catch/catchtreeitem.h23
6 files changed, 176 insertions, 17 deletions
diff --git a/src/plugins/autotest/catch/catchcodeparser.cpp b/src/plugins/autotest/catch/catchcodeparser.cpp
index 44f3632b83..6261460b4f 100644
--- a/src/plugins/autotest/catch/catchcodeparser.cpp
+++ b/src/plugins/autotest/catch/catchcodeparser.cpp
@@ -45,9 +45,9 @@ CatchCodeParser::CatchCodeParser(const QByteArray &source, const LanguageFeature
{
}
-static TestCodeLocationAndType locationAndTypeFromToken(const Token &tkn)
+static CatchTestCodeLocationAndType locationAndTypeFromToken(const Token &tkn)
{
- TestCodeLocationAndType locationAndType;
+ CatchTestCodeLocationAndType locationAndType;
locationAndType.m_type = TestTreeItem::TestFunction;
locationAndType.m_line = tkn.lineno;
locationAndType.m_column = 0;
@@ -77,13 +77,12 @@ static QStringList parseTags(const QString &tagsString)
return tagsList;
}
-TestCodeLocationList CatchCodeParser::findTests()
+CatchTestCodeLocationList CatchCodeParser::findTests()
{
m_tokens = tokensForSource(m_source, m_features);
m_currentIndex = 0;
for ( ; m_currentIndex < m_tokens.size(); ++m_currentIndex) {
- const Token &token = m_tokens.at(m_currentIndex);
- if (token.kind() == T_IDENTIFIER)
+ if (m_tokens.at(m_currentIndex).kind() == T_IDENTIFIER)
handleIdentifier();
}
return m_testCases;
@@ -98,6 +97,17 @@ void CatchCodeParser::handleIdentifier()
handleTestCase(false);
} else if (identifier == "SCENARIO") {
handleTestCase(true);
+ } else if (identifier == "TEMPLATE_TEST_CASE" || identifier == "TEMPLATE_PRODUCT_TEST_CASE"
+ || identifier == "TEMPLATE_LIST_TEST_CASE" || identifier == "TEMPLATE_TEST_CASE_SIG"
+ || identifier == "TEMPLATE_PRODUCT_TEST_CASE_SIG") {
+ handleParameterizedTestCase(false);
+ } else if (identifier == "TEST_CASE_METHOD") {
+ handleFixtureTestCase();
+ } else if (identifier == "TEMPLATE_TEST_CASE_METHOD_SIG"
+ || identifier == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"
+ || identifier == "TEMPLATE_TEST_CASE_METHOD"
+ || identifier == "TEMPLATE_LIST_TEST_CASE_METHOD") {
+ handleParameterizedTestCase(true);
}
}
@@ -106,8 +116,8 @@ void CatchCodeParser::handleTestCase(bool isScenario)
if (!skipCommentsUntil(T_LPAREN))
return;
- Token token = m_tokens.at(m_currentIndex);
- TestCodeLocationAndType locationAndType = locationAndTypeFromToken(token);
+ CatchTestCodeLocationAndType locationAndType
+ = locationAndTypeFromToken(m_tokens.at(m_currentIndex));
Kind stoppedAt;
++m_currentIndex;
@@ -126,6 +136,72 @@ void CatchCodeParser::handleTestCase(bool isScenario)
testCaseName.prepend("Scenario: "); // use a flag?
locationAndType.m_name = testCaseName;
+ locationAndType.tags = parseTags(tagsString);
+ m_testCases.append(locationAndType);
+}
+
+void CatchCodeParser::handleParameterizedTestCase(bool isFixture)
+{
+ if (!skipCommentsUntil(T_LPAREN))
+ return;
+
+ if (isFixture && !skipFixtureParameter())
+ return;
+
+ CatchTestCodeLocationAndType locationAndType
+ = locationAndTypeFromToken(m_tokens.at(m_currentIndex));
+
+ Kind stoppedAt;
+ ++m_currentIndex;
+ QString testCaseName = getStringLiteral(stoppedAt);
+ QString tagsString;
+
+ if (stoppedAt != T_COMMA)
+ return;
+
+ ++m_currentIndex;
+ tagsString = getStringLiteral(stoppedAt);
+
+ if (stoppedAt == T_COMMA)
+ stoppedAt = skipUntilCorrespondingRParen();
+
+ if (stoppedAt != T_RPAREN)
+ return;
+ locationAndType.m_name = testCaseName;
+ locationAndType.tags = parseTags(tagsString);
+ locationAndType.states = CatchTreeItem::Parameterized;
+ if (isFixture)
+ locationAndType.states |= CatchTreeItem::Fixture;
+ m_testCases.append(locationAndType);
+}
+
+void CatchCodeParser::handleFixtureTestCase()
+{
+ if (!skipCommentsUntil(T_LPAREN))
+ return;
+
+ if (!skipFixtureParameter())
+ return;
+
+ CatchTestCodeLocationAndType locationAndType
+ = locationAndTypeFromToken(m_tokens.at(m_currentIndex));
+
+ Kind stoppedAt;
+ ++m_currentIndex;
+ QString testCaseName = getStringLiteral(stoppedAt);
+ QString tagsString;
+
+ if (stoppedAt == T_COMMA) {
+ ++m_currentIndex;
+ tagsString = getStringLiteral(stoppedAt);
+ }
+
+ if (stoppedAt != T_RPAREN)
+ return;
+
+ locationAndType.m_name = testCaseName;
+ locationAndType.tags = parseTags(tagsString);
+ locationAndType.states = CatchTreeItem::Fixture;
m_testCases.append(locationAndType);
}
@@ -166,5 +242,30 @@ bool CatchCodeParser::skipCommentsUntil(Kind nextExpectedKind)
return false;
}
+Kind CatchCodeParser::skipUntilCorrespondingRParen()
+{
+ int openParens = 1; // we have already one open, looking for the corresponding closing
+ int end = m_tokens.size();
+ while (m_currentIndex < end) {
+ Kind kind = m_tokens.at(m_currentIndex).kind();
+ if (kind == T_LPAREN) {
+ ++openParens;
+ } else if (kind == T_RPAREN) {
+ --openParens;
+ if (openParens == 0)
+ return T_RPAREN;
+ }
+ ++m_currentIndex;
+ }
+ return T_ERROR;
+}
+
+bool CatchCodeParser::skipFixtureParameter()
+{
+ if (!skipCommentsUntil(T_IDENTIFIER))
+ return false;
+ return skipCommentsUntil(T_COMMA);
+}
+
} // namespace Internal
} // namespace Autotest
diff --git a/src/plugins/autotest/catch/catchcodeparser.h b/src/plugins/autotest/catch/catchcodeparser.h
index f8f2052927..b10a682087 100644
--- a/src/plugins/autotest/catch/catchcodeparser.h
+++ b/src/plugins/autotest/catch/catchcodeparser.h
@@ -41,13 +41,17 @@ public:
CatchCodeParser(const QByteArray &source, const CPlusPlus::LanguageFeatures &features,
const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot);
virtual ~CatchCodeParser() = default;
- TestCodeLocationList findTests();
+ CatchTestCodeLocationList findTests();
private:
void handleIdentifier();
void handleTestCase(bool isScenario);
+ void handleParameterizedTestCase(bool isFixture);
+ void handleFixtureTestCase();
QString getStringLiteral(CPlusPlus::Kind &stoppedAtKind);
bool skipCommentsUntil(CPlusPlus::Kind nextExpectedKind); // moves currentIndex if succeeds
+ CPlusPlus::Kind skipUntilCorrespondingRParen(); // moves currentIndex
+ bool skipFixtureParameter();
const QByteArray &m_source;
const CPlusPlus::LanguageFeatures &m_features;
@@ -55,7 +59,7 @@ private:
const CPlusPlus::Snapshot &m_snapshot;
CPlusPlus::Tokens m_tokens;
int m_currentIndex = 0;
- TestCodeLocationList m_testCases;
+ CatchTestCodeLocationList m_testCases;
int m_lineNo = 0;
};
diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp
index 18740ac4ad..689dafe339 100644
--- a/src/plugins/autotest/catch/catchtestparser.cpp
+++ b/src/plugins/autotest/catch/catchtestparser.cpp
@@ -39,7 +39,16 @@ namespace Internal {
static bool isCatchTestCaseMacro(const QString &macroName)
{
const QStringList validTestCaseMacros = {
- QStringLiteral("TEST_CASE"), QStringLiteral("SCENARIO")
+ QStringLiteral("TEST_CASE"), QStringLiteral("SCENARIO"),
+ QStringLiteral("TEMPLATE_TEST_CASE"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE"),
+ QStringLiteral("TEMPLATE_LIST_TEST_CASE"),
+ QStringLiteral("TEMPLATE_TEST_CASE_SIG"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_SIG"),
+ QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
+ QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD"),
+ QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD_SIG"),
+ QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"),
+ QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
+ QStringLiteral("TEMPLATE_LIST_TEST_CASE_METHOD")
};
return validTestCaseMacros.contains(macroName);
}
@@ -102,7 +111,7 @@ static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInter
proFile = projectPart->projectFile;
CatchCodeParser codeParser(fileContent, projectPart->languageFeatures, doc, snapshot);
- const TestCodeLocationList foundTests = codeParser.findTests();
+ const CatchTestCodeLocationList foundTests = codeParser.findTests();
CatchParseResult *parseResult = new CatchParseResult(framework);
parseResult->itemType = TestTreeItem::TestCase;
@@ -111,7 +120,7 @@ static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInter
parseResult->displayName = filePath;
parseResult->proFile = projectParts.first()->projectFile;
- for (const TestCodeLocationAndType & testLocation : foundTests) {
+ for (const CatchTestCodeLocationAndType & testLocation : foundTests) {
CatchParseResult *testCase = new CatchParseResult(framework);
testCase->fileName = filePath;
testCase->name = testLocation.m_name;
@@ -119,6 +128,7 @@ static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInter
testCase->itemType = testLocation.m_type;
testCase->line = testLocation.m_line;
testCase->column = testLocation.m_column;
+ testCase->states = testLocation.states;
parseResult->children.append(testCase);
}
@@ -146,6 +156,7 @@ TestTreeItem *CatchParseResult::createTestTreeItem() const
item->setProFile(proFile);
item->setLine(line);
item->setColumn(column);
+ item->setStates(states);
for (const TestParseResult *testSet : children)
item->appendChild(testSet->createTestTreeItem());
diff --git a/src/plugins/autotest/catch/catchtestparser.h b/src/plugins/autotest/catch/catchtestparser.h
index 704c3491af..4c558aa637 100644
--- a/src/plugins/autotest/catch/catchtestparser.h
+++ b/src/plugins/autotest/catch/catchtestparser.h
@@ -24,6 +24,7 @@
#pragma once
+#include "catchtreeitem.h"
#include "../itestparser.h"
namespace Autotest {
@@ -35,6 +36,7 @@ public:
explicit CatchParseResult(ITestFramework *framework)
: TestParseResult(framework) {}
TestTreeItem *createTestTreeItem() const override;
+ CatchTreeItem::TestStates states;
};
class CatchTestParser : public CppParser
diff --git a/src/plugins/autotest/catch/catchtreeitem.cpp b/src/plugins/autotest/catch/catchtreeitem.cpp
index 5068f15320..7b97621d17 100644
--- a/src/plugins/autotest/catch/catchtreeitem.cpp
+++ b/src/plugins/autotest/catch/catchtreeitem.cpp
@@ -33,6 +33,11 @@
namespace Autotest {
namespace Internal {
+QString CatchTreeItem::testCasesString() const
+{
+ return m_state & CatchTreeItem::Parameterized ? QString(name() + " -*") : name();
+}
+
QVariant CatchTreeItem::data(int column, int role) const
{
@@ -40,7 +45,7 @@ QVariant CatchTreeItem::data(int column, int role) const
case Qt::DisplayRole:
if (type() == Root)
break;
- return name();
+ return QString(name() + stateSuffix());
case Qt::CheckStateRole:
switch (type()) {
case Root:
@@ -148,7 +153,7 @@ TestConfiguration *CatchTreeItem::testConfiguration() const
config->setTestCaseCount(childCount());
config->setProjectFile(proFile());
config->setProject(project);
- config->setTestCases(QStringList(name()));
+ config->setTestCases(QStringList(testCasesString()));
config->setInternalTargets(internalTargets());
return config;
}
@@ -185,14 +190,16 @@ static void collectTestInfo(const TestTreeItem *item,
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());
+ CatchTreeItem *current = static_cast<CatchTreeItem *>(it);
+ testCasesForProfile[projectFile].names.append(current->testCasesString());
});
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());
+ CatchTreeItem *current = static_cast<CatchTreeItem *>(child);
+ testCasesForProfile[child->proFile()].names.append(current->testCasesString());
testCasesForProfile[child->proFile()].internalTargets.unite(
child->internalTargets());
}
@@ -230,7 +237,8 @@ QList<TestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Uti
QStringList testCases;
item->forFirstLevelChildren([&testCases](TestTreeItem *child) {
- testCases << child->name();
+ CatchTreeItem *current = static_cast<CatchTreeItem *>(child);
+ testCases << current->testCasesString();
});
testConfig = new CatchConfiguration(framework());
@@ -244,6 +252,16 @@ QList<TestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Uti
return result;
}
+QString CatchTreeItem::stateSuffix() const
+{
+ QStringList types;
+ if (m_state & CatchTreeItem::Parameterized)
+ types.append(QCoreApplication::translate("CatchTreeItem", "parameterized"));
+ if (m_state & CatchTreeItem::Fixture)
+ types.append(QCoreApplication::translate("CatchTreeItem", "fixture"));
+ return types.isEmpty() ? QString() : QString(" [" + types.join(", ") + ']');
+}
+
QList<TestConfiguration *> CatchTreeItem::getTestConfigurations(bool ignoreCheckState) const
{
QList<TestConfiguration *> result;
diff --git a/src/plugins/autotest/catch/catchtreeitem.h b/src/plugins/autotest/catch/catchtreeitem.h
index c9708acdba..b52fc9417d 100644
--- a/src/plugins/autotest/catch/catchtreeitem.h
+++ b/src/plugins/autotest/catch/catchtreeitem.h
@@ -32,10 +32,22 @@ namespace Internal {
class CatchTreeItem : public TestTreeItem
{
public:
+ enum TestState
+ {
+ Normal = 0x0,
+ Parameterized = 0x1,
+ Fixture = 0x2
+ };
+ Q_FLAGS(TestState)
+ Q_DECLARE_FLAGS(TestStates, TestState)
+
explicit CatchTreeItem(ITestFramework *framework, const QString &name = QString(),
const QString &filePath = QString(), Type type = Root)
: TestTreeItem(framework, name, filePath, type) {}
+ void setStates(CatchTreeItem::TestStates state) { m_state = state; }
+ QString testCasesString() const;
+
QVariant data(int column, int role) const override;
TestTreeItem *copyWithoutChildren() override;
@@ -53,8 +65,19 @@ public:
QList<TestConfiguration *> getTestConfigurationsForFile(const Utils::FilePath &fileName) const override;
private:
+ QString stateSuffix() const;
QList<TestConfiguration *> getTestConfigurations(bool ignoreCheckState) const;
+ TestStates m_state = Normal;
};
+class CatchTestCodeLocationAndType : public TestCodeLocationAndType
+{
+public:
+ CatchTreeItem::TestStates states = CatchTreeItem::Normal;
+ QStringList tags; // TODO: use them for the item
+};
+
+typedef QVector<CatchTestCodeLocationAndType> CatchTestCodeLocationList;
+
} // namespace Internal
} // namespace Autotest