File: ctestsuite.cpp

package info (click to toggle)
kdevelop 4%3A25.04.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 73,508 kB
  • sloc: cpp: 291,803; python: 4,322; javascript: 3,518; sh: 1,316; ansic: 703; xml: 414; php: 95; lisp: 66; makefile: 31; sed: 12
file content (226 lines) | stat: -rw-r--r-- 7,960 bytes parent folder | download | duplicates (2)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
    SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "ctestsuite.h"
#include "ctestrunjob.h"
#include <debug_testing.h>

#include <interfaces/itestcontroller.h>
#include <interfaces/iproject.h>
#include <language/duchain/duchain.h>
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchainutils.h>
#include <language/duchain/declaration.h>
#include <language/duchain/indexeddeclaration.h>
#include <language/duchain/classdeclaration.h>
#include <language/duchain/classfunctiondeclaration.h>
#include <language/duchain/functiondeclaration.h>
#include <language/duchain/functiondefinition.h>
#include <language/duchain/types/functiontype.h>
#include <language/duchain/types/pointertype.h>
#include <language/duchain/types/referencetype.h>
#include <language/duchain/types/structuretype.h>

using namespace KDevelop;

CTestSuite::CTestSuite(const QString& name, const KDevelop::Path &executable, const QList<KDevelop::Path>& files, IProject* project, const QStringList& args, const QHash<QString, QString>& properties):
m_executable(executable),
m_name(name),
m_args(args),
m_files(files),
m_project(project),
m_properties(properties)
{
    Q_ASSERT(project);
    qCDebug(CMAKE_TESTING) << m_name << m_executable << m_project->name();
}

CTestSuite::~CTestSuite()
{

}

bool CTestSuite::findCaseDeclarations(const QVector<Declaration*> &classDeclarations)
{
    for (Declaration* decl : classDeclarations) {
        qCDebug(CMAKE_TESTING) << "Found declaration" << decl->toString()
                               << decl->identifier().identifier().byteArray();

        const auto* const function = dynamic_cast<ClassFunctionDeclaration*>(decl);
        if (!function || !(function->accessPolicy() == Declaration::Private && function->isSlot())) {
            continue;
        }
        QString name = function->qualifiedIdentifier().last().toString();
        qCDebug(CMAKE_TESTING) << "Found private slot in test" << name;

        if (name.endsWith(QLatin1String("_data"))) {
            continue;
        }

        const auto functionType = function->type<FunctionType>();
        if (!functionType || functionType->indexedArgumentsSize() > 0) {
            // function declarations with arguments are not valid test functions
            continue;
        }
        qCDebug(CMAKE_TESTING) << "Found test case function declaration" << function->identifier().toString();

        if (name != QLatin1String("initTestCase") && name != QLatin1String("cleanupTestCase") &&
            name != QLatin1String("init") && name != QLatin1String("cleanup"))
        {
            m_cases << name;
        } else {
            continue;
        }

        const auto* def = FunctionDefinition::definition(decl);
        m_declarations[name] = def ? IndexedDeclaration(def) : IndexedDeclaration(function);
    }
    return !m_declarations.isEmpty();
}

void CTestSuite::loadDeclarations(const IndexedString& document, const KDevelop::ReferencedTopDUContext& ref)
{
    DUChainReadLocker locker(DUChain::lock());
    TopDUContext* topContext = DUChainUtils::contentContextFromProxyContext(ref.data());
    if (!topContext) {
        qCDebug(CMAKE_TESTING) << "No top context in" << document.str();
        return;
    }

    Declaration* testClass = nullptr;
    const auto mainId = Identifier(QStringLiteral("main"));
    const auto mainDeclarations = topContext->findLocalDeclarations(mainId);
    DUContext* tmpInternalContext;
    for (Declaration* declaration : mainDeclarations) {
        if (!declaration->isDefinition() || !(tmpInternalContext = declaration->internalContext())) {
            continue;
        }
        RangeInRevision contextRange = tmpInternalContext->range();
        qCDebug(CMAKE_TESTING) << "Found a definition for a function 'main()' at" << contextRange;

        /*
            * This function tries to deduce the test class from the main function definition of
            * the test source file. To do so, the cursor is set before the end of the internal
            * context. Going backwards from there, the first variable declaration of a class
            * type with private slots is assumed to be the test class.
            * This method finds test classes passed to QTEST_MAIN or QTEST_GUILESS_MAIN, but
            * also some classes which are used in manual implementations with a similar structure
            * as in the Qt macros.
            * If no such class is found, the main function definition is linked to the test suite
            * and no test cases are added to the test suite.
        */

        --contextRange.end.column; // set cursor before the end of the definition
        const auto innerContext = topContext->findContextAt(contextRange.end, true);
        if (!innerContext) {
            continue;
        }
        const auto mainDeclarations = innerContext->localDeclarations(topContext);
        for (auto it = mainDeclarations.rbegin(); it != mainDeclarations.rend(); ++it) {
            qCDebug(CMAKE_TESTING) << "Main declaration" << (*it)->toString();

            auto type = (*it)->abstractType();
            // Strip pointer and reference types to finally get to the structure type of the test class
            while (type && (type->whichType() == AbstractType::TypePointer || type->whichType() == AbstractType::TypeReference)) {
                if (const auto ptype = type.dynamicCast<PointerType>()) {
                    type = ptype->baseType();
                } else if (const auto rtype = type.dynamicCast<ReferenceType>()) {
                    type = rtype->baseType();
                } else {
                    type = nullptr;
                }
            }
            const auto structureType = type.dynamicCast<StructureType>();
            if (!structureType) {
                continue;
            }

            testClass = structureType->declaration(topContext);
            if (!testClass || !(tmpInternalContext = testClass->internalContext())) {
                continue;
            }

            if (findCaseDeclarations(tmpInternalContext->localDeclarations(topContext))) {
                break;
            }
        }
        testClass = declaration;
    }

    if (testClass && testClass->internalContext()) {
        m_suiteDeclaration = IndexedDeclaration(testClass);
    } else {
        qCDebug(CMAKE_TESTING) << "No test class found or internal context missing in " << document.str();
    }
}

KJob* CTestSuite::launchCase(const QString& testCase, TestJobVerbosity verbosity)
{
    return launchCases(QStringList() << testCase, verbosity);
}

KJob* CTestSuite::launchCases(const QStringList& testCases, ITestSuite::TestJobVerbosity verbosity)
{
    qCDebug(CMAKE_TESTING) << "Launching test run" << m_name << "with cases" << testCases;

    OutputJob::OutputJobVerbosity outputVerbosity = (verbosity == Verbose) ? OutputJob::Verbose : OutputJob::Silent;
    return new CTestRunJob(this, testCases, outputVerbosity);
}

KJob* CTestSuite::launchAllCases(TestJobVerbosity verbosity)
{
    return launchCases(cases(), verbosity);
}

KDevelop::Path CTestSuite::executable() const
{
    return m_executable;
}

QStringList CTestSuite::cases() const
{
    return m_cases;
}

QString CTestSuite::name() const
{
    return m_name;
}

KDevelop::IProject* CTestSuite::project() const
{
    return m_project;
}

QStringList CTestSuite::arguments() const
{
    return m_args;
}

IndexedDeclaration CTestSuite::declaration() const
{
    return m_suiteDeclaration;
}

IndexedDeclaration CTestSuite::caseDeclaration(const QString& testCase) const
{
    return m_declarations.value(testCase, IndexedDeclaration(nullptr));
}

void CTestSuite::setTestCases(const QStringList& cases)
{
    m_cases = cases;
}

QList<KDevelop::Path> CTestSuite::sourceFiles() const
{
    return m_files;
}

QHash<QString, QString> CTestSuite::properties() const
{
    return m_properties;
}