diff --git a/plugins/autotest/autotest.pro b/plugins/autotest/autotest.pro index 1a411d34908c2276a7808735ed01661b62094639..f360aac5b8eeca9e8c6507303e86a5ceec6ba306 100644 --- a/plugins/autotest/autotest.pro +++ b/plugins/autotest/autotest.pro @@ -38,6 +38,7 @@ HEADERS += \ testcodeparser.h \ autotestplugin.h \ autotest_global.h \ + autotest_utils.h \ autotestconstants.h \ testrunner.h \ testconfiguration.h \ diff --git a/plugins/autotest/autotest.qbs b/plugins/autotest/autotest.qbs index 704394044c7eeaa90b0910e218a8b358ddf4259d..b5696184e7e5fe413539cda9ce33e9fd15e4d2d8 100644 --- a/plugins/autotest/autotest.qbs +++ b/plugins/autotest/autotest.qbs @@ -32,6 +32,7 @@ QtcCommercialPlugin { "autotest.qrc", "autotesticons.h", "autotest_global.h", + "autotest_utils.h", "autotestconstants.h", "autotestplugin.cpp", "autotestplugin.h", diff --git a/plugins/autotest/autotest_utils.h b/plugins/autotest/autotest_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a9f1ba45bf6bf141f28c8cc6de403ab44883434a --- /dev/null +++ b/plugins/autotest/autotest_utils.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at +** http://www.qt.io/contact-us +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://www.qt.io/contact-us +** +****************************************************************************/ + +#ifndef AUTOTEST_UTILS_H +#define AUTOTEST_UTILS_H + +#include <QStringList> + +namespace AutoTest { +namespace Internal { + +static bool isGTestMacro(const QString ¯o) +{ + static QStringList valid = { + QStringLiteral("TEST"), QStringLiteral("TEST_F"), QStringLiteral("TEST_P") + }; + return valid.contains(macro); +} + +} // namespace Internal +} // namespace AutoTest + +#endif // AUTOTEST_UTILS_H diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp index 6f32e8dc582d6b38973a8c78e7c9a6ee9c846e05..bf1aab0399b345ae2691f3c68cc67cf164716921 100644 --- a/plugins/autotest/testcodeparser.cpp +++ b/plugins/autotest/testcodeparser.cpp @@ -18,6 +18,7 @@ ****************************************************************************/ #include "autotestconstants.h" +#include "autotest_utils.h" #include "testcodeparser.h" #include "testinfo.h" #include "testvisitor.h" @@ -223,6 +224,26 @@ static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc, return false; } +static bool includesGTest(const CPlusPlus::Document::Ptr &doc, + const CppTools::CppModelManager *cppMM) +{ + const QString gtestH = QLatin1String("gtest/gtest.h"); + foreach (const CPlusPlus::Document::Include &inc, doc->resolvedIncludes()) { + if (inc.resolvedFileName().endsWith(gtestH)) + return true; + } + + if (cppMM) { + const CPlusPlus::Snapshot snapshot = cppMM->snapshot(); + foreach (const QString &include, snapshot.allIncludesForDocument(doc->fileName())) { + if (include.endsWith(gtestH)) + return true; + } + } + + return false; +} + static bool qtTestLibDefined(const CppTools::CppModelManager *cppMM, const QString &fileName) { @@ -298,6 +319,24 @@ static QString quickTestName(const CPlusPlus::Document::Ptr &doc) return QString(); } +static QSet<QString> testNames(CPlusPlus::Document::Ptr &document) +{ + QSet<QString> result; + foreach (const CPlusPlus::Document::MacroUse ¯o, document->macroUses()) { + if (!macro.isFunctionLike()) + continue; + if (AutoTest::Internal::isGTestMacro(QLatin1String(macro.macro().name()))) { + const QVector<CPlusPlus::Document::Block> args = macro.arguments(); + if (args.size() != 2) + continue; + const CPlusPlus::Document::Block name = args.first(); + result.insert(QLatin1String(getFileContent(document->fileName()) + .mid(name.bytesBegin(), name.bytesEnd() - name.bytesBegin()))); + } + } + return result; +} + static QList<QmlJS::Document::Ptr> scanDirectoryForQuickTestQmlFiles(const QString &srcDir) { QStringList dirs(srcDir); @@ -494,6 +533,15 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document) return; } } + + if (includesGTest(document, modelManager)) { + const QSet<QString> &names = testNames(document); + if (!names.isEmpty()) { + handleGTest(document->fileName(), names); + return; + } + } + // could not find the class to test, or QTest is not included and QT_TESTLIB_LIB defined // maybe file is only a referenced file if (m_cppDocMap.contains(fileName)) { @@ -547,6 +595,20 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) } } +void TestCodeParser::handleGTest(const QString &filePath, const QSet<QString> &names) +{ + const QByteArray &fileContent = getFileContent(filePath); + const CPlusPlus::Snapshot snapshot = CPlusPlus::CppModelManagerBase::instance()->snapshot(); + CPlusPlus::Document::Ptr document = snapshot.preprocessedDocument(fileContent, filePath); + document->check(); + CPlusPlus::AST *ast = document->translationUnit()->ast(); + GTestVisitor visitor(document); + visitor.accept(ast); + + QMap<QString, TestCodeLocationList> result = visitor.gtestFunctions(); + QTC_CHECK(names.contains(result.keys().toSet())); +} + void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document) { if (m_codeModelParsing) { diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h index 3c5529f63a667513b7feede10fcce4dd23596d53..d966b363d4b7ac2b5e01c688af14f3f7389e713e 100644 --- a/plugins/autotest/testcodeparser.h +++ b/plugins/autotest/testcodeparser.h @@ -82,6 +82,7 @@ public slots: void updateTestTree(); void checkDocumentForTestCode(CPlusPlus::Document::Ptr document); void handleQtQuickTest(CPlusPlus::Document::Ptr document); + void handleGTest(const QString &filePath, const QSet<QString> &names); void onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document); void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h index 19cd4adf7e5356f6bdd33d0cabf19d0b4a53fe56..c228b325a6dd01bce46fbe2732f0b92df1494afa 100644 --- a/plugins/autotest/testtreeitem.h +++ b/plugins/autotest/testtreeitem.h @@ -47,7 +47,10 @@ public: TestFunction, TestDataTag, TestDataFunction, - TestSpecialFunction + TestSpecialFunction, + GTestCase, // should we distinguish between Case and Fixture? + GTestName, + GTestNameDisabled }; TestTreeItem(const QString &name = QString(), const QString &filePath = QString(), diff --git a/plugins/autotest/testvisitor.cpp b/plugins/autotest/testvisitor.cpp index 6074d76ac9ea03afd9c94131dae04c71422d2033..9a0c51961d688624e78c5ed67593664c5b2ee44f 100644 --- a/plugins/autotest/testvisitor.cpp +++ b/plugins/autotest/testvisitor.cpp @@ -17,6 +17,7 @@ ** ****************************************************************************/ +#include "autotest_utils.h" #include "testvisitor.h" #include <cplusplus/FullySpecifiedType.h> @@ -339,5 +340,51 @@ bool TestQmlVisitor::visit(QmlJS::AST::StringLiteral *ast) return false; } +/********************** Google Test Function AST Visitor **********************/ + +GTestVisitor::GTestVisitor(CPlusPlus::Document::Ptr doc) + : CPlusPlus::ASTVisitor(doc->translationUnit()) + , m_document(doc) +{ +} + +bool GTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast) +{ + if (!ast || !ast->declarator || !ast->declarator->core_declarator) + return false; + + CPlusPlus::DeclaratorIdAST *id = ast->declarator->core_declarator->asDeclaratorId(); + if (!id || !ast->symbol || ast->symbol->argumentCount() != 2) + return false; + + CPlusPlus::LookupContext lc; + const QString prettyName = m_overview.prettyName(lc.fullyQualifiedName(ast->symbol)); + if (!AutoTest::Internal::isGTestMacro(prettyName)) + return false; + + CPlusPlus::Argument *testCaseNameArg = ast->symbol->argumentAt(0)->asArgument(); + CPlusPlus::Argument *testNameArg = ast->symbol->argumentAt(1)->asArgument(); + if (testCaseNameArg && testNameArg) { + const QString &testCaseName = m_overview.prettyType(testCaseNameArg->type()); + const QString &testName = m_overview.prettyType(testNameArg->type()); + + bool disabled = testName.startsWith(QLatin1String("DISABLED_")); + unsigned line = 0; + unsigned column = 0; + unsigned token = id->firstToken(); + m_document->translationUnit()->getTokenStartPosition(token, &line, &column); + + TestCodeLocationAndType locationAndType; + locationAndType.m_name = disabled ? testName.mid(9) : testName; + locationAndType.m_line = line; + locationAndType.m_column = column - 1; + locationAndType.m_type = disabled ? TestTreeItem::GTestNameDisabled + : TestTreeItem::GTestName; + m_gtestFunctions[testCaseName].append(locationAndType); + } + + return false; +} + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testvisitor.h b/plugins/autotest/testvisitor.h index 4441eb26f8ab1f97f3015debf00ee49515dfb0ed..bbccffd4e18165105eaf7bd809ca3c0f81297640 100644 --- a/plugins/autotest/testvisitor.h +++ b/plugins/autotest/testvisitor.h @@ -123,6 +123,21 @@ private: }; +class GTestVisitor : public CPlusPlus::ASTVisitor +{ +public: + GTestVisitor(CPlusPlus::Document::Ptr doc); + bool visit(CPlusPlus::FunctionDefinitionAST *ast); + + QMap<QString, TestCodeLocationList> gtestFunctions() const { return m_gtestFunctions; } + +private: + CPlusPlus::Document::Ptr m_document; + CPlusPlus::Overview m_overview; + QMap<QString, TestCodeLocationList> m_gtestFunctions; + +}; + } // namespace Internal } // namespace Autotest