aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/autotest/gtest/gtestparser.cpp
blob: d5eb7bfc09a51cb2d75b5e0e5e782d70c1affb3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0

#include "gtestparser.h"

#include "gtesttreeitem.h"
#include "gtestvisitors.h"
#include "gtest_utils.h"

#include <cppeditor/cppmodelmanager.h>
#include <cppeditor/projectpart.h>

#include <QRegularExpression>
#include <QRegularExpressionMatch>

namespace Autotest {
namespace Internal {

TestTreeItem *GTestParseResult::createTestTreeItem() const
{
    if (itemType != TestTreeItem::TestSuite && itemType != TestTreeItem::TestCase)
        return nullptr;
    GTestTreeItem *item = new GTestTreeItem(framework, name, fileName, itemType);
    item->setProFile(proFile);
    item->setLine(line);
    item->setColumn(column);

    if (parameterized)
        item->setState(GTestTreeItem::Parameterized);
    if (typed)
        item->setState(GTestTreeItem::Typed);
    if (disabled)
        item->setState(GTestTreeItem::Disabled);
    for (const TestParseResult *testSet : children)
        item->appendChild(testSet->createTestTreeItem());
    return item;
}

static bool includesGTest(const CPlusPlus::Document::Ptr &doc,
                          const CPlusPlus::Snapshot &snapshot)
{
    static const QString gtestH("gtest/gtest.h");
    for (const CPlusPlus::Document::Include &inc : doc->resolvedIncludes()) {
        if (inc.resolvedFileName().endsWith(gtestH))
            return true;
    }

    for (const QString &include : snapshot.allIncludesForDocument(doc->fileName())) {
        if (include.endsWith(gtestH))
            return true;
    }

    return CppParser::precompiledHeaderContains(snapshot,
                                                Utils::FilePath::fromString(doc->fileName()),
                                                gtestH);
}

static bool hasGTestNames(const CPlusPlus::Document::Ptr &document)
{
    for (const CPlusPlus::Document::MacroUse &macro : document->macroUses()) {
        if (!macro.isFunctionLike())
            continue;
        if (GTestUtils::isGTestMacro(QLatin1String(macro.macro().name()))) {
            const QVector<CPlusPlus::Document::Block> args = macro.arguments();
            if (args.size() != 2)
                continue;
            return true;
        }
    }
    return false;
}

bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futureInterface,
                                  const Utils::FilePath &fileName)
{
    CPlusPlus::Document::Ptr doc = document(fileName);
    if (doc.isNull() || !includesGTest(doc, m_cppSnapshot))
        return false;

    const QByteArray &fileContent = getFileContent(fileName);
    if (!hasGTestNames(doc)) {
        const QRegularExpression regex("\\b(TEST(_[FP])?|TYPED_TEST(_P)?|(GTEST_TEST))");
        if (!regex.match(QString::fromUtf8(fileContent)).hasMatch())
            return false;
    }

    const QString &filePath = doc->fileName();
    const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance();
    CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName);
    document->check();
    CPlusPlus::AST *ast = document->translationUnit()->ast();
    GTestVisitor visitor(document);
    visitor.accept(ast);

    const QMap<GTestCaseSpec, GTestCodeLocationList> result = visitor.gtestFunctions();
    Utils::FilePath proFile;
    const QList<CppEditor::ProjectPart::ConstPtr> &ppList = modelManager->projectPart(filePath);
    if (!ppList.isEmpty())
        proFile = Utils::FilePath::fromString(ppList.first()->projectFile);
    else
        return false; // happens if shutting down while parsing

    for (auto it = result.cbegin(); it != result.cend(); ++it) {
        const GTestCaseSpec &testSpec = it.key();
        GTestParseResult *parseResult = new GTestParseResult(framework());
        parseResult->itemType = TestTreeItem::TestSuite;
        parseResult->fileName = fileName;
        parseResult->name = testSpec.testCaseName;
        parseResult->parameterized = testSpec.parameterized;
        parseResult->typed = testSpec.typed;
        parseResult->disabled = testSpec.disabled;
        parseResult->proFile = proFile;

        for (const GTestCodeLocationAndType &location : it.value()) {
            GTestParseResult *testSet = new GTestParseResult(framework());
            testSet->name = location.m_name;
            testSet->fileName = fileName;
            testSet->line = location.m_line;
            testSet->column = location.m_column;
            testSet->disabled = location.m_state & GTestTreeItem::Disabled;
            testSet->itemType = location.m_type;
            testSet->proFile = proFile;

            parseResult->children.append(testSet);
        }

        futureInterface.reportResult(TestParseResultPtr(parseResult));
    }
    return !result.isEmpty();
}

} // namespace Internal
} // namespace Autotest