1 /* This file is part of KDevelop
2 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
3 SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6 */
7
8 #include "duchaintestbase.h"
9
10 #include <QtTest/QtTest>
11
12 #include <language/duchain/parsingenvironment.h>
13 #include <language/duchain/duchainlock.h>
14 #include <language/duchain/topducontext.h>
15 #include <language/duchain/indexedstring.h>
16 #include <language/duchain/dumpchain.h>
17 #include <kstandarddirs.h>
18 #include <kcomponentdata.h>
19
20 #include "dumptypes.h"
21 #include "parsesession.h"
22 #include "phpdebugvisitor.h"
23 #include "../builders/declarationbuilder.h"
24 #include "../builders/usebuilder.h"
25 #include "../helper.h"
26 #include <completion/codemodelitem.h>
27
28 #include <tests/autotestshell.h>
29 #include <tests/testcore.h>
30 #include <language/codegen/coderepresentation.h>
31
32 using namespace KDevelop;
33
34
35 namespace Php
36 {
37
DUChainTestBase()38 DUChainTestBase::DUChainTestBase()
39 {
40 KComponentData kd("kdevphpsupport");
41 }
42
initTestCase()43 void DUChainTestBase::initTestCase()
44 {
45 AutoTestShell::init();
46 TestCore::initialize(Core::NoUi);
47
48 DUChain::self()->disablePersistentStorage();
49 CodeRepresentation::setDiskChangesForbidden(true);
50
51 //yeah... adding a testcase here is kinda strange, but anyways - we have to check for special
52 //handling of the internal functions file
53 //see e.g. testDeclarationReturnTypeDocBlock
54 QByteArray content("<?php "
55 "class Exception {} "
56 //test start
57 "/** @return Exception **/ function should_return_exception() {}\n"
58 "class internal_test_class {/** @return Exception **/ function should_return_exception() {}}\n"
59 // test end
60 "function define() {} function substr() {} class stdClass {}\n"
61 "/**\n * @superglobal\n **/\n$_GET = array();\n"
62 "interface testInterface {}\n");
63 content.append("interface Iterator { function rewind(); function current(); function key(); function next(); function valid(); } ");
64 TopDUContext* ctx = parseAdditionalFile(internalFunctionFile(), content);
65 QVERIFY(ctx);
66
67 DUChainWriteLocker lock;
68 QVERIFY(ctx->problems().isEmpty());
69
70 // set features and modification revision, to prevent test cases that use
71 // the full language plugin from re-parsing the big internal function file
72 ctx->setFeatures(TopDUContext::AllDeclarationsAndContexts);
73 ParsingEnvironmentFilePointer file = ctx->parsingEnvironmentFile();
74 QVERIFY(file);
75 file->setModificationRevision(ModificationRevision::revisionForFile(internalFunctionFile()));
76 DUChain::self()->updateContextEnvironment(ctx, file.data());
77 }
78
cleanupTestCase()79 void DUChainTestBase::cleanupTestCase()
80 {
81 TestCore::shutdown();
82 }
83
searchDeclaration(QList<CompletionTreeItemPointer> items,Declaration * declaration)84 CompletionTreeItemPointer DUChainTestBase::searchDeclaration(QList<CompletionTreeItemPointer> items, Declaration* declaration)
85 {
86 foreach(const CompletionTreeItemPointer &item, items) {
87 if (item->declaration().data() == declaration) {
88 return item;
89 }
90 }
91 return CompletionTreeItemPointer();
92 }
93
hasImportedParentContext(TopDUContext * top,DUContext * lookingFor)94 bool DUChainTestBase::hasImportedParentContext(TopDUContext* top, DUContext* lookingFor)
95 {
96 kDebug() << "this topcontext has " << top->importedParentContexts().count() << " imported parent contexts"
97 << "\n we are looking for: " << lookingFor->url().byteArray();
98 foreach(const DUContext::Import &import, top->importedParentContexts()) {
99 if (import.context(top)) {
100 kDebug() << import.context(top)->url().byteArray();
101 }
102 if (import.context(top) == lookingFor) {
103 return true;
104 }
105 }
106 return false;
107 }
108
parseAdditionalFile(const IndexedString & fileName,const QByteArray & contents)109 TopDUContext* DUChainTestBase::parseAdditionalFile(const IndexedString& fileName, const QByteArray& contents)
110 {
111 ParseSession session;
112 session.setContents(contents);
113 StartAst* ast = 0;
114 if (!session.parse(&ast)) qFatal("can't parse");
115
116 EditorIntegrator editor(&session);
117 session.setCurrentDocument(fileName);
118 DeclarationBuilder declarationBuilder(&editor);
119 TopDUContext* top = declarationBuilder.build(fileName, ast);
120
121 if ( fileName != internalFunctionFile() ) {
122 UseBuilder useBuilder(&editor);
123 useBuilder.buildUses(ast);
124 }
125
126 if (!session.problems().isEmpty()) {
127 DUChainWriteLocker lock;
128 foreach( const ProblemPointer& p, session.problems() ) {
129 top->addProblem(p);
130 }
131 }
132
133 return top;
134 }
135
parse(const QByteArray & unit,DumpAreas dump,QString url,TopDUContext * update)136 TopDUContext* DUChainTestBase::parse(const QByteArray& unit, DumpAreas dump,
137 QString url, TopDUContext* update)
138 {
139 if (dump)
140 kDebug() << "==== Beginning new test case...:" << endl << unit;
141
142 ParseSession session;
143 session.setContents(unit);
144 StartAst* ast = 0;
145 if (!session.parse(&ast)) {
146 kDebug() << "Parse failed";
147 return 0;
148 }
149
150 if (dump & DumpAST) {
151 kDebug() << "===== AST:";
152 DebugVisitor debugVisitor(session.tokenStream(), session.contents());
153 debugVisitor.visitNode(ast);
154 }
155
156 static int testNumber = 0;
157 if (url.isEmpty()) url = QString("file:///internal/%1").arg(testNumber++);
158
159 EditorIntegrator editor(&session);
160 session.setCurrentDocument(IndexedString(url));
161 DeclarationBuilder declarationBuilder(&editor);
162 TopDUContext* top = declarationBuilder.build(session.currentDocument(), ast, ReferencedTopDUContext(update));
163
164 if (!session.problems().isEmpty()) {
165 DUChainWriteLocker lock;
166 foreach( const ProblemPointer& p, session.problems() ) {
167 top->addProblem(p);
168 }
169 }
170
171 if ( IndexedString(url) != internalFunctionFile() ) {
172 UseBuilder useBuilder(&editor);
173 useBuilder.buildUses(ast);
174 }
175
176 if (dump & DumpDUChain) {
177 kDebug() << "===== DUChain:";
178
179 DUChainWriteLocker lock(DUChain::lock());
180 dumpDUContext(top);
181 }
182
183 if (dump & DumpType) {
184 kDebug() << "===== Types:";
185 DUChainWriteLocker lock(DUChain::lock());
186 DumpTypes dt;
187 foreach(const AbstractType::Ptr& type, declarationBuilder.topTypes()) {
188 dt.dump(type.unsafeData());
189 }
190 }
191
192
193
194 if (dump)
195 kDebug() << "===== Finished test case.";
196
197 return top;
198 }
199 }
200
201 #include "duchaintestbase.moc"
202
203