Changeset View
Changeset View
Standalone View
Standalone View
plugins/cmake/testing/ctestsuite.cpp
Show All 29 Lines | |||||
30 | #include <language/duchain/declaration.h> | 30 | #include <language/duchain/declaration.h> | ||
31 | #include <language/duchain/classfunctiondeclaration.h> | 31 | #include <language/duchain/classfunctiondeclaration.h> | ||
32 | #include <language/duchain/functiondeclaration.h> | 32 | #include <language/duchain/functiondeclaration.h> | ||
33 | #include <language/duchain/functiondefinition.h> | 33 | #include <language/duchain/functiondefinition.h> | ||
34 | #include <language/duchain/duchainutils.h> | 34 | #include <language/duchain/duchainutils.h> | ||
35 | #include <language/duchain/types/structuretype.h> | 35 | #include <language/duchain/types/structuretype.h> | ||
36 | #include <project/projectmodel.h> | 36 | #include <project/projectmodel.h> | ||
37 | 37 | | |||
38 | Declaration* findTestClassDeclaration(const CursorInRevision& c, DUContext* ctx, RangeInRevision::ContainsBehavior behavior) | ||||
39 | { | ||||
40 | /* | ||||
41 | * This code is mostly copied from DUChainUtils::itemUnderCursorInternal. | ||||
42 | * However, it is simplified because we are only looking for uses of the test class, | ||||
43 | * so we can skip the search through local declarations, which speeds up the search. | ||||
44 | * Additionally, we are only interested in the declaration itself, not in its context | ||||
45 | * or range. | ||||
46 | */ | ||||
47 | | ||||
48 | foreach(DUContext* subCtx, ctx->childContexts()) | ||||
49 | { | ||||
50 | //This is a little hacky, but we need it in case of foreach macros and similar stuff | ||||
51 | if(subCtx->range().contains(c, behavior) || subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) | ||||
52 | { | ||||
53 | Declaration *d = findTestClassDeclaration(c, subCtx, behavior); | ||||
54 | if (d) | ||||
55 | { | ||||
56 | return d; | ||||
57 | } | ||||
58 | } | ||||
59 | } | ||||
60 | | ||||
61 | for(int a = 0; a < ctx->usesCount(); ++a) | ||||
62 | { | ||||
63 | if(ctx->uses()[a].m_range.contains(c, behavior)) | ||||
64 | { | ||||
65 | return ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex); | ||||
66 | } | ||||
67 | } | ||||
68 | | ||||
69 | return nullptr; | ||||
70 | } | ||||
38 | 71 | | |||
39 | using namespace KDevelop; | 72 | using namespace KDevelop; | ||
40 | 73 | | |||
41 | CTestSuite::CTestSuite(const QString& name, const KDevelop::Path &executable, const QList<KDevelop::Path>& files, IProject* project, const QStringList& args, const QHash<QString, QString>& properties): | 74 | CTestSuite::CTestSuite(const QString& name, const KDevelop::Path &executable, const QList<KDevelop::Path>& files, IProject* project, const QStringList& args, const QHash<QString, QString>& properties): | ||
42 | m_executable(executable), | 75 | m_executable(executable), | ||
43 | m_name(name), | 76 | m_name(name), | ||
44 | m_args(args), | 77 | m_args(args), | ||
45 | m_files(files), | 78 | m_files(files), | ||
Show All 16 Lines | 92 | { | |||
62 | if (!topContext) | 95 | if (!topContext) | ||
63 | { | 96 | { | ||
64 | qCDebug(CMAKE) << "No top context in" << document.str(); | 97 | qCDebug(CMAKE) << "No top context in" << document.str(); | ||
65 | return; | 98 | return; | ||
66 | } | 99 | } | ||
67 | 100 | | |||
68 | Declaration* testClass = nullptr; | 101 | Declaration* testClass = nullptr; | ||
69 | Identifier testCaseIdentifier(QStringLiteral("tc")); | 102 | Identifier testCaseIdentifier(QStringLiteral("tc")); | ||
70 | foreach (Declaration* declaration, topContext->findLocalDeclarations(Identifier("main"))) | 103 | | ||
104 | foreach (Declaration* declaration, topContext->findLocalDeclarations(Identifier(QStringLiteral("main")))) | ||||
71 | { | 105 | { | ||
72 | if (declaration->isDefinition()) | 106 | if (declaration->isDefinition()) | ||
73 | { | 107 | { | ||
74 | qCDebug(CMAKE) << "Found a definition for a function 'main()' "; | 108 | qCDebug(CMAKE) << "Found a definition for a function 'main()' at" << declaration->range(); | ||
75 | FunctionDefinition* def = dynamic_cast<FunctionDefinition*>(declaration); | 109 | | ||
76 | DUContext* main = def->internalContext(); | 110 | /* | ||
77 | foreach (Declaration* mainDeclaration, main->localDeclarations(topContext)) | 111 | * This is a rather hacky soluction to get the test class for a Qt test. | ||
78 | { | 112 | * | ||
79 | if (mainDeclaration->identifier() == testCaseIdentifier) | 113 | * The class is used as the argument to the QTEST_MAIN or QTEST_GUILESS_MAIN macro. | ||
80 | { | 114 | * This macro expands to a main() function with a variable declaration with 'tc' as | ||
81 | qCDebug(CMAKE) << "Found tc declaration in main:" << mainDeclaration->identifier().toString(); | 115 | * the name and with the test class as the type. | ||
82 | qCDebug(CMAKE) << "Its type is" << mainDeclaration->abstractType()->toString(); | 116 | * | ||
83 | if (StructureType::Ptr type = mainDeclaration->abstractType().cast<StructureType>()) | 117 | * Unfortunately, we cannot get to the function body context in order to find | ||
118 | * this variable declaration. | ||||
119 | * Instead, we find the cursor to the beginning of the main() function, offset | ||||
120 | * the cursor to the inside of the QTEST_MAIN(x) call, and find the declaration there. | ||||
121 | * If it is a type declaration, that type is the main test class. | ||||
122 | */ | ||||
123 | | ||||
124 | CursorInRevision cursor = declaration->range().start; | ||||
125 | Declaration* testClassDeclaration = nullptr; | ||||
126 | int mainDeclarationColumn = cursor.column; | ||||
127 | | ||||
128 | // cursor points to the start of QTEST_MAIN(x) invocation, we offset it to point inside it | ||||
129 | cursor.column += 12; | ||||
130 | testClassDeclaration = findTestClassDeclaration(cursor, topContext, RangeInRevision::Default); | ||||
131 | | ||||
132 | while (!testClassDeclaration || testClassDeclaration->kind() != Declaration::Kind::Type) | ||||
133 | { | ||||
134 | // If the first found declaration was not a type, the macro may be QTEST_GUILESS_MAIN rather than QTEST_MAIN. | ||||
135 | // Alternatively, it may be called as QTEST_MAIN(KDevelop::TestCase), or something similar. | ||||
136 | // So we just try a couple of different positions. | ||||
137 | cursor.column += 8; | ||||
138 | if (cursor.column > mainDeclarationColumn + 60) | ||||
139 | { | ||||
140 | break; | ||||
141 | } | ||||
142 | testClassDeclaration = findTestClassDeclaration(cursor, topContext, RangeInRevision::Default); | ||||
143 | } | ||||
144 | | ||||
145 | if (testClassDeclaration && testClassDeclaration->kind() == Declaration::Kind::Type) | ||||
146 | { | ||||
147 | qCDebug(CMAKE) << "Found test class declaration" << testClassDeclaration->identifier().toString() << testClassDeclaration->kind(); | ||||
148 | if (StructureType::Ptr type = testClassDeclaration->type<StructureType>()) | ||||
84 | { | 149 | { | ||
85 | testClass = type->declaration(topContext); | 150 | testClass = type->declaration(topContext); | ||
151 | if (testClass && testClass->internalContext()) | ||||
152 | { | ||||
153 | break; | ||||
86 | } | 154 | } | ||
87 | } | 155 | } | ||
88 | } | 156 | } | ||
89 | } | 157 | } | ||
90 | } | 158 | } | ||
91 | 159 | | |||
92 | if (!testClass || !testClass->internalContext()) | 160 | if (!testClass || !testClass->internalContext()) | ||
93 | { | 161 | { | ||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |