2019-01-02 16:11:44 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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"
|
|
|
|
|
|
2020-04-09 13:59:04 +02:00
|
|
|
#include "catchcodeparser.h"
|
2020-10-06 15:27:27 +02:00
|
|
|
#include "catchframework.h"
|
2019-01-02 16:11:44 +01:00
|
|
|
#include "catchtreeitem.h"
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
#include <cppeditor/cppmodelmanager.h>
|
|
|
|
|
#include <cppeditor/projectpart.h>
|
2019-01-02 16:11:44 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
|
|
|
|
|
namespace Autotest {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
static bool isCatchTestCaseMacro(const QString ¯oName)
|
|
|
|
|
{
|
|
|
|
|
const QStringList validTestCaseMacros = {
|
2020-04-14 13:02:33 +02:00
|
|
|
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"),
|
2020-04-16 11:49:49 +02:00
|
|
|
QStringLiteral("TEMPLATE_LIST_TEST_CASE_METHOD"),
|
|
|
|
|
QStringLiteral("METHOD_AS_TEST_CASE"), QStringLiteral("REGISTER_TEST_CASE")
|
2019-01-02 16:11:44 +01:00
|
|
|
};
|
|
|
|
|
return validTestCaseMacros.contains(macroName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isCatchMacro(const QString ¯oName)
|
|
|
|
|
{
|
2022-06-10 10:46:35 +02:00
|
|
|
QString unprefixed = macroName.startsWith("CATCH_") ? macroName.mid(6) : macroName;
|
2019-01-02 16:11:44 +01:00
|
|
|
const QStringList validSectionMacros = {
|
|
|
|
|
QStringLiteral("SECTION"), QStringLiteral("WHEN")
|
|
|
|
|
};
|
2022-06-10 10:46:35 +02:00
|
|
|
return isCatchTestCaseMacro(unprefixed) || validSectionMacros.contains(unprefixed);
|
2019-01-02 16:11:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool includesCatchHeader(const CPlusPlus::Document::Ptr &doc,
|
|
|
|
|
const CPlusPlus::Snapshot &snapshot)
|
|
|
|
|
{
|
2021-04-14 09:46:29 +02:00
|
|
|
static const QStringList catchHeaders{"catch.hpp", // v2
|
|
|
|
|
"catch_all.hpp", // v3 - new approach
|
|
|
|
|
"catch_amalgamated.hpp",
|
|
|
|
|
"catch_test_macros.hpp",
|
|
|
|
|
"catch_template_test_macros.hpp"
|
|
|
|
|
};
|
2019-01-02 16:11:44 +01:00
|
|
|
for (const CPlusPlus::Document::Include &inc : doc->resolvedIncludes()) {
|
2021-04-14 09:46:29 +02:00
|
|
|
for (const QString &catchHeader : catchHeaders) {
|
|
|
|
|
if (inc.resolvedFileName().endsWith(catchHeader))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-01-02 16:11:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const QString &include : snapshot.allIncludesForDocument(doc->fileName())) {
|
2021-04-14 09:46:29 +02:00
|
|
|
for (const QString &catchHeader : catchHeaders) {
|
|
|
|
|
if (include.endsWith(catchHeader))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-01-02 16:11:44 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-04 14:28:03 +02:00
|
|
|
for (const QString &catchHeader : catchHeaders) {
|
|
|
|
|
if (CppParser::precompiledHeaderContains(snapshot,
|
|
|
|
|
Utils::FilePath::fromString(doc->fileName()),
|
|
|
|
|
catchHeader))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-01-02 16:11:44 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-27 09:59:07 +02:00
|
|
|
bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
|
2021-05-26 15:50:03 +02:00
|
|
|
const Utils::FilePath &fileName)
|
2019-01-02 16:11:44 +01:00
|
|
|
{
|
2021-01-01 19:19:10 +01:00
|
|
|
CPlusPlus::Document::Ptr doc = document(fileName);
|
2021-06-04 14:28:03 +02:00
|
|
|
if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot))
|
2021-01-01 19:19:10 +01:00
|
|
|
return false;
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance();
|
2019-01-02 16:11:44 +01:00
|
|
|
const QString &filePath = doc->fileName();
|
2021-05-26 15:50:03 +02:00
|
|
|
const QByteArray &fileContent = getFileContent(fileName);
|
2019-01-02 16:11:44 +01:00
|
|
|
|
2021-06-04 14:28:03 +02:00
|
|
|
if (!hasCatchNames(doc)) {
|
2022-06-10 10:46:35 +02:00
|
|
|
const QRegularExpression regex("\\b(CATCH_)?"
|
|
|
|
|
"(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
|
2021-06-04 14:28:03 +02:00
|
|
|
"TEMPLATE_TEST_CASE(_METHOD)?_SIG|"
|
|
|
|
|
"TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|"
|
|
|
|
|
"TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|"
|
|
|
|
|
"REGISTER_TEST_CASE)");
|
|
|
|
|
if (!regex.match(QString::fromUtf8(fileContent)).hasMatch())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-30 10:58:08 +02:00
|
|
|
const QList<CppEditor::ProjectPart::ConstPtr> projectParts = modelManager->projectPart(fileName);
|
2020-04-09 13:59:04 +02:00
|
|
|
if (projectParts.isEmpty()) // happens if shutting down while parsing
|
2019-01-02 16:11:44 +01:00
|
|
|
return false;
|
2021-05-26 15:50:03 +02:00
|
|
|
Utils::FilePath proFile;
|
2021-08-30 10:58:08 +02:00
|
|
|
const CppEditor::ProjectPart::ConstPtr projectPart = projectParts.first();
|
2021-05-26 15:50:03 +02:00
|
|
|
proFile = Utils::FilePath::fromString(projectPart->projectFile);
|
2020-04-09 13:59:04 +02:00
|
|
|
|
2020-04-23 12:38:27 +02:00
|
|
|
CatchCodeParser codeParser(fileContent, projectPart->languageFeatures);
|
2020-04-14 13:02:33 +02:00
|
|
|
const CatchTestCodeLocationList foundTests = codeParser.findTests();
|
2019-01-02 16:11:44 +01:00
|
|
|
|
2021-01-01 19:19:10 +01:00
|
|
|
CatchParseResult *parseResult = new CatchParseResult(framework());
|
2020-04-16 10:38:05 +02:00
|
|
|
parseResult->itemType = TestTreeItem::TestSuite;
|
2021-05-26 15:50:03 +02:00
|
|
|
parseResult->fileName = fileName;
|
2019-01-02 16:11:44 +01:00
|
|
|
parseResult->name = filePath;
|
|
|
|
|
parseResult->displayName = filePath;
|
2021-05-26 15:50:03 +02:00
|
|
|
parseResult->proFile = proFile;
|
2019-01-02 16:11:44 +01:00
|
|
|
|
2020-04-14 13:02:33 +02:00
|
|
|
for (const CatchTestCodeLocationAndType & testLocation : foundTests) {
|
2021-01-01 19:19:10 +01:00
|
|
|
CatchParseResult *testCase = new CatchParseResult(framework());
|
2021-05-26 15:50:03 +02:00
|
|
|
testCase->fileName = fileName;
|
2020-04-09 13:59:04 +02:00
|
|
|
testCase->name = testLocation.m_name;
|
2019-01-02 16:11:44 +01:00
|
|
|
testCase->proFile = proFile;
|
2020-04-09 13:59:04 +02:00
|
|
|
testCase->itemType = testLocation.m_type;
|
2019-01-02 16:11:44 +01:00
|
|
|
testCase->line = testLocation.m_line;
|
|
|
|
|
testCase->column = testLocation.m_column;
|
2020-04-14 13:02:33 +02:00
|
|
|
testCase->states = testLocation.states;
|
2019-01-02 16:11:44 +01:00
|
|
|
|
|
|
|
|
parseResult->children.append(testCase);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
futureInterface.reportResult(TestParseResultPtr(parseResult));
|
|
|
|
|
|
2020-04-09 13:59:04 +02:00
|
|
|
return !foundTests.isEmpty();
|
2019-01-02 16:11:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestTreeItem *CatchParseResult::createTestTreeItem() const
|
|
|
|
|
{
|
|
|
|
|
if (itemType == TestTreeItem::Root)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
2021-01-25 16:31:16 +01:00
|
|
|
CatchTreeItem *item = new CatchTreeItem(framework, name, fileName, itemType);
|
2019-01-02 16:11:44 +01:00
|
|
|
item->setProFile(proFile);
|
|
|
|
|
item->setLine(line);
|
|
|
|
|
item->setColumn(column);
|
2020-04-14 13:02:33 +02:00
|
|
|
item->setStates(states);
|
2019-01-02 16:11:44 +01:00
|
|
|
|
|
|
|
|
for (const TestParseResult *testSet : children)
|
|
|
|
|
item->appendChild(testSet->createTestTreeItem());
|
|
|
|
|
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Autotest
|