/**************************************************************************** ** ** 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 "catchcodeparser.h" #include "catchframework.h" #include "catchtreeitem.h" #include #include #include #include namespace Autotest { namespace Internal { static bool isCatchTestCaseMacro(const QString ¯oName) { const QStringList validTestCaseMacros = { 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"), QStringLiteral("METHOD_AS_TEST_CASE"), QStringLiteral("REGISTER_TEST_CASE") }; return validTestCaseMacros.contains(macroName); } static bool isCatchMacro(const QString ¯oName) { const QStringList validSectionMacros = { QStringLiteral("SECTION"), QStringLiteral("WHEN") }; return isCatchTestCaseMacro(macroName) || validSectionMacros.contains(macroName); } static bool includesCatchHeader(const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) { 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" }; for (const CPlusPlus::Document::Include &inc : doc->resolvedIncludes()) { for (const QString &catchHeader : catchHeaders) { if (inc.resolvedFileName().endsWith(catchHeader)) return true; } } for (const QString &include : snapshot.allIncludesForDocument(doc->fileName())) { for (const QString &catchHeader : catchHeaders) { if (include.endsWith(catchHeader)) return true; } } for (const QString &catchHeader : catchHeaders) { if (CppParser::precompiledHeaderContains(snapshot, Utils::FilePath::fromString(doc->fileName()), 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 args = macro.arguments(); if (args.size() < 1) continue; return true; } } return false; } bool CatchTestParser::processDocument(QFutureInterface &futureInterface, const Utils::FilePath &fileName) { CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot)) return false; const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); const QString &filePath = doc->fileName(); const QByteArray &fileContent = getFileContent(fileName); if (!hasCatchNames(doc)) { const QRegularExpression regex("\\b(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|" "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; } const QList projectParts = modelManager->projectPart(fileName); if (projectParts.isEmpty()) // happens if shutting down while parsing return false; Utils::FilePath proFile; const CppEditor::ProjectPart::ConstPtr projectPart = projectParts.first(); proFile = Utils::FilePath::fromString(projectPart->projectFile); CatchCodeParser codeParser(fileContent, projectPart->languageFeatures); const CatchTestCodeLocationList foundTests = codeParser.findTests(); CatchParseResult *parseResult = new CatchParseResult(framework()); parseResult->itemType = TestTreeItem::TestSuite; parseResult->fileName = fileName; parseResult->name = filePath; parseResult->displayName = filePath; parseResult->proFile = proFile; for (const CatchTestCodeLocationAndType & testLocation : foundTests) { CatchParseResult *testCase = new CatchParseResult(framework()); testCase->fileName = fileName; testCase->name = testLocation.m_name; testCase->proFile = proFile; testCase->itemType = testLocation.m_type; testCase->line = testLocation.m_line; testCase->column = testLocation.m_column; testCase->states = testLocation.states; parseResult->children.append(testCase); } futureInterface.reportResult(TestParseResultPtr(parseResult)); return !foundTests.isEmpty(); } 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); item->setStates(states); for (const TestParseResult *testSet : children) item->appendChild(testSet->createTestTreeItem()); return item; } } // namespace Internal } // namespace Autotest