forked from qt-creator/qt-creator
AutoTest: Re-implement catch parser
The AST handling is limited and fails for other macros that will be added later on. Replace the parser by using a lexer. Change-Id: Ia11f0a05eec770c703180935a64615e5090b314c Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -24,12 +24,11 @@
|
||||
|
||||
#include "catchtestparser.h"
|
||||
|
||||
#include "catchcodeparser.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>
|
||||
@@ -37,12 +36,6 @@
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct CatchTestCaseSpec
|
||||
{
|
||||
QString name;
|
||||
QStringList tags;
|
||||
};
|
||||
|
||||
static bool isCatchTestCaseMacro(const QString ¯oName)
|
||||
{
|
||||
const QStringList validTestCaseMacros = {
|
||||
@@ -59,123 +52,6 @@ static bool isCatchMacro(const QString ¯oName)
|
||||
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)
|
||||
{
|
||||
@@ -217,37 +93,30 @@ static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInter
|
||||
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
|
||||
const QList<CppTools::ProjectPart::Ptr> projectParts = modelManager->projectPart(filePath);
|
||||
if (projectParts.isEmpty()) // happens if shutting down while parsing
|
||||
return false;
|
||||
QString proFile;
|
||||
const CppTools::ProjectPart::Ptr projectPart = projectParts.first();
|
||||
proFile = projectPart->projectFile;
|
||||
|
||||
CatchCodeParser codeParser(fileContent, projectPart->languageFeatures, doc, snapshot);
|
||||
const TestCodeLocationList foundTests = codeParser.findTests();
|
||||
|
||||
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);
|
||||
for (const TestCodeLocationAndType & testLocation : foundTests) {
|
||||
CatchParseResult *testCase = new CatchParseResult(framework);
|
||||
testCase->fileName = filePath;
|
||||
testCase->name = testSpec.name;
|
||||
testCase->name = testLocation.m_name;
|
||||
testCase->proFile = proFile;
|
||||
testCase->itemType = TestTreeItem::TestFunction;
|
||||
testCase->itemType = testLocation.m_type;
|
||||
testCase->line = testLocation.m_line;
|
||||
testCase->column = testLocation.m_column;
|
||||
|
||||
@@ -256,7 +125,7 @@ static bool handleCatchDocument(QFutureInterface<TestParseResultPtr> futureInter
|
||||
|
||||
futureInterface.reportResult(TestParseResultPtr(parseResult));
|
||||
|
||||
return !result.keys().isEmpty();
|
||||
return !foundTests.isEmpty();
|
||||
}
|
||||
|
||||
bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> futureInterface, const QString &fileName)
|
||||
|
||||
Reference in New Issue
Block a user