OLD | NEW |
| (Empty) |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'dart:async'; | |
6 | |
7 import 'package:analysis_server/src/services/index/index.dart'; | |
8 import 'package:analyzer/dart/ast/ast.dart'; | |
9 import 'package:analyzer/dart/element/element.dart'; | |
10 import 'package:analyzer/src/generated/source.dart'; | |
11 import 'package:analyzer/src/summary/idl.dart'; | |
12 import 'package:test/test.dart'; | |
13 import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
14 import 'package:typed_mock/typed_mock.dart'; | |
15 | |
16 import '../../abstract_single_unit.dart'; | |
17 | |
18 main() { | |
19 defineReflectiveSuite(() { | |
20 defineReflectiveTests(IndexTest); | |
21 }); | |
22 } | |
23 | |
24 @reflectiveTest | |
25 class IndexTest extends AbstractSingleUnitTest { | |
26 Index index = createMemoryIndex(); | |
27 | |
28 /** | |
29 * Return the [Location] with given properties, or fail. | |
30 */ | |
31 Location findLocation(List<Location> locations, String libraryUri, | |
32 String unitUri, int offset, int length, bool isQualified) { | |
33 for (Location location in locations) { | |
34 if (location.libraryUri == libraryUri && | |
35 location.unitUri == unitUri && | |
36 location.offset == offset && | |
37 location.length == length && | |
38 location.isQualified == isQualified) { | |
39 return location; | |
40 } | |
41 } | |
42 fail('No at $offset with length $length qualified=$isQualified in\n' | |
43 '${locations.join('\n')}'); | |
44 return null; | |
45 } | |
46 | |
47 /** | |
48 * Return the [Location] with given properties, or fail. | |
49 */ | |
50 Location findLocationSource( | |
51 List<Location> locations, Source source, String search, bool isQualified, | |
52 {int length}) { | |
53 String code = source.contents.data; | |
54 int offset = code.indexOf(search); | |
55 expect(offset, isNonNegative, reason: 'Not found "$search" in\n$code'); | |
56 length ??= getLeadingIdentifierLength(search); | |
57 String uri = source.uri.toString(); | |
58 return findLocation(locations, uri, uri, offset, length, isQualified); | |
59 } | |
60 | |
61 /** | |
62 * Return the [Location] with given properties, or fail. | |
63 */ | |
64 Location findLocationTest( | |
65 List<Location> locations, String search, bool isQualified, | |
66 {int length}) { | |
67 int offset = findOffset(search); | |
68 length ??= getLeadingIdentifierLength(search); | |
69 String testUri = testSource.uri.toString(); | |
70 return findLocation( | |
71 locations, testUri, testUri, offset, length, isQualified); | |
72 } | |
73 | |
74 void setUp() { | |
75 super.setUp(); | |
76 } | |
77 | |
78 void tearDown() { | |
79 super.tearDown(); | |
80 index = null; | |
81 } | |
82 | |
83 test_getDefinedNames_classMember() async { | |
84 await _indexTestUnit(''' | |
85 class A { | |
86 test() {} | |
87 } | |
88 class B { | |
89 int test = 1; | |
90 main() { | |
91 int test = 2; | |
92 } | |
93 } | |
94 '''); | |
95 ClassElement classA = findElement('A'); | |
96 ClassElement classB = findElement('B'); | |
97 List<Location> locations = await index.getDefinedNames( | |
98 new RegExp(r'^test$'), IndexNameKind.classMember); | |
99 expect(locations, hasLength(2)); | |
100 _assertHasDefinedName(locations, classA.methods[0]); | |
101 _assertHasDefinedName(locations, classB.fields[0]); | |
102 } | |
103 | |
104 test_getDefinedNames_topLevel() async { | |
105 await _indexTestUnit(''' | |
106 class A {} // A | |
107 class B = Object with A; | |
108 typedef C(); | |
109 D() {} | |
110 var E = null; | |
111 class NoMatchABCDE {} | |
112 '''); | |
113 Element topA = findElement('A'); | |
114 Element topB = findElement('B'); | |
115 Element topC = findElement('C'); | |
116 Element topD = findElement('D'); | |
117 Element topE = findElement('E'); | |
118 List<Location> locations = await index.getDefinedNames( | |
119 new RegExp(r'^[A-E]$'), IndexNameKind.topLevel); | |
120 expect(locations, hasLength(5)); | |
121 _assertHasDefinedName(locations, topA); | |
122 _assertHasDefinedName(locations, topB); | |
123 _assertHasDefinedName(locations, topC); | |
124 _assertHasDefinedName(locations, topD); | |
125 _assertHasDefinedName(locations, topE); | |
126 } | |
127 | |
128 test_getDefinedNames_topLevel2() async { | |
129 await _indexTestUnit( | |
130 ''' | |
131 class A {} // A | |
132 class B = Object with A; | |
133 class NoMatchABCDE {} | |
134 ''', | |
135 declOnly: true); | |
136 Element topA = findElement('A'); | |
137 Element topB = findElement('B'); | |
138 List<Location> locations = await index.getDefinedNames( | |
139 new RegExp(r'^[A-E]$'), IndexNameKind.topLevel); | |
140 expect(locations, hasLength(2)); | |
141 _assertHasDefinedName(locations, topA); | |
142 _assertHasDefinedName(locations, topB); | |
143 } | |
144 | |
145 test_getRelations_isExtendedBy() async { | |
146 await _indexTestUnit(r''' | |
147 class A {} | |
148 class B extends A {} // B | |
149 '''); | |
150 Source source2 = await _indexUnit( | |
151 '/test2.dart', | |
152 r''' | |
153 import 'test.dart'; | |
154 class C extends A {} // C | |
155 '''); | |
156 ClassElement elementA = testUnitElement.getType('A'); | |
157 List<Location> locations = | |
158 await index.getRelations(elementA, IndexRelationKind.IS_EXTENDED_BY); | |
159 findLocationTest(locations, 'A {} // B', false); | |
160 findLocationSource(locations, source2, 'A {} // C', false); | |
161 } | |
162 | |
163 test_getRelations_isReferencedBy() async { | |
164 await _indexTestUnit(r''' | |
165 main(int a, int b) { | |
166 } | |
167 '''); | |
168 ClassElement intElement = | |
169 testUnitElement.context.typeProvider.intType.element; | |
170 List<Location> locations = await index.getRelations( | |
171 intElement, IndexRelationKind.IS_REFERENCED_BY); | |
172 findLocationTest(locations, 'int a', false); | |
173 findLocationTest(locations, 'int b', false); | |
174 } | |
175 | |
176 test_getUnresolvedMemberReferences_qualified_resolved() async { | |
177 await _indexTestUnit(''' | |
178 class A { | |
179 var test; // A | |
180 } | |
181 main(A a) { | |
182 print(a.test); | |
183 a.test = 1; | |
184 a.test += 2; | |
185 a.test(); | |
186 } | |
187 '''); | |
188 List<Location> locations = | |
189 await index.getUnresolvedMemberReferences('test'); | |
190 expect(locations, isEmpty); | |
191 } | |
192 | |
193 test_getUnresolvedMemberReferences_qualified_unresolved() async { | |
194 await _indexTestUnit(''' | |
195 class A { | |
196 var test; // A | |
197 } | |
198 main(p) { | |
199 print(p.test); | |
200 p.test = 1; | |
201 p.test += 2; | |
202 p.test(); | |
203 print(p.test2); // not requested | |
204 } | |
205 '''); | |
206 List<Location> locations = | |
207 await index.getUnresolvedMemberReferences('test'); | |
208 expect(locations, hasLength(4)); | |
209 findLocationTest(locations, 'test);', true); | |
210 findLocationTest(locations, 'test = 1;', true); | |
211 findLocationTest(locations, 'test += 2;', true); | |
212 findLocationTest(locations, 'test();', true); | |
213 } | |
214 | |
215 test_getUnresolvedMemberReferences_unqualified_resolved() async { | |
216 await _indexTestUnit(''' | |
217 class A { | |
218 var test; | |
219 m() { | |
220 print(test); | |
221 test = 1; | |
222 test += 2; | |
223 test(); | |
224 } | |
225 } | |
226 '''); | |
227 List<Location> locations = | |
228 await index.getUnresolvedMemberReferences('test'); | |
229 expect(locations, isEmpty); | |
230 } | |
231 | |
232 test_getUnresolvedMemberReferences_unqualified_unresolved() async { | |
233 verifyNoTestUnitErrors = false; | |
234 await _indexTestUnit(''' | |
235 class A { | |
236 m() { | |
237 print(test); | |
238 test = 1; | |
239 test += 2; | |
240 test(); | |
241 print(test2); // not requested | |
242 } | |
243 } | |
244 '''); | |
245 List<Location> locations = | |
246 await index.getUnresolvedMemberReferences('test'); | |
247 expect(locations, hasLength(4)); | |
248 findLocationTest(locations, 'test);', false); | |
249 findLocationTest(locations, 'test = 1;', false); | |
250 findLocationTest(locations, 'test += 2;', false); | |
251 findLocationTest(locations, 'test();', false); | |
252 } | |
253 | |
254 test_indexDeclarations_afterIndexUnit() async { | |
255 await resolveTestUnit(''' | |
256 var a = 0; | |
257 var b = a + 1; | |
258 '''); | |
259 index.indexUnit(testUnit); | |
260 TopLevelVariableElement a = findElement('a'); | |
261 // We can find references. | |
262 { | |
263 List<Location> locations = await index.getRelations( | |
264 a.getter, IndexRelationKind.IS_REFERENCED_BY); | |
265 findLocationTest(locations, 'a + 1', false); | |
266 } | |
267 // Attempt to index just declarations - we still can find references. | |
268 index.indexDeclarations(testUnit); | |
269 { | |
270 List<Location> locations = await index.getRelations( | |
271 a.getter, IndexRelationKind.IS_REFERENCED_BY); | |
272 findLocationTest(locations, 'a + 1', false); | |
273 } | |
274 } | |
275 | |
276 test_indexDeclarations_nullUnit() async { | |
277 index.indexDeclarations(null); | |
278 } | |
279 | |
280 test_indexDeclarations_nullUnitElement() async { | |
281 await resolveTestUnit(''); | |
282 testUnit.element = null; | |
283 index.indexDeclarations(testUnit); | |
284 } | |
285 | |
286 test_indexUnit_nullLibraryElement() async { | |
287 await resolveTestUnit(''); | |
288 CompilationUnitElement unitElement = new _CompilationUnitElementMock(); | |
289 expect(unitElement.library, isNull); | |
290 testUnit.element = unitElement; | |
291 index.indexUnit(testUnit); | |
292 } | |
293 | |
294 test_indexUnit_nullUnit() async { | |
295 index.indexUnit(null); | |
296 } | |
297 | |
298 test_indexUnit_nullUnitElement() async { | |
299 await resolveTestUnit(''); | |
300 testUnit.element = null; | |
301 index.indexUnit(testUnit); | |
302 } | |
303 | |
304 test_removeContext() async { | |
305 await _indexTestUnit(''' | |
306 class A {} | |
307 '''); | |
308 RegExp regExp = new RegExp(r'^A$'); | |
309 expect(await index.getDefinedNames(regExp, IndexNameKind.topLevel), | |
310 hasLength(1)); | |
311 // remove the context - no top-level declarations | |
312 index.removeContext(testUnitElement.context); | |
313 expect( | |
314 await index.getDefinedNames(regExp, IndexNameKind.topLevel), isEmpty); | |
315 } | |
316 | |
317 test_removeUnit() async { | |
318 RegExp regExp = new RegExp(r'^[AB]$'); | |
319 Source sourceA = addSource('/a.dart', 'class A {}'); | |
320 Source sourceB = addSource('/b.dart', 'class B {}'); | |
321 CompilationUnit unitA = await resolveLibraryUnit(sourceA); | |
322 CompilationUnit unitB = await resolveLibraryUnit(sourceB); | |
323 index.indexUnit(unitA); | |
324 index.indexUnit(unitB); | |
325 { | |
326 List<Location> locations = | |
327 await index.getDefinedNames(regExp, IndexNameKind.topLevel); | |
328 expect(locations, hasLength(2)); | |
329 expect(locations.map((l) => l.libraryUri), | |
330 unorderedEquals([sourceA.uri.toString(), sourceB.uri.toString()])); | |
331 } | |
332 // remove a.dart - no a.dart location | |
333 index.removeUnit(unitA.element.context, sourceA, sourceA); | |
334 { | |
335 List<Location> locations = | |
336 await index.getDefinedNames(regExp, IndexNameKind.topLevel); | |
337 expect(locations, hasLength(1)); | |
338 expect(locations.map((l) => l.libraryUri), | |
339 unorderedEquals([sourceB.uri.toString()])); | |
340 } | |
341 } | |
342 | |
343 /** | |
344 * Assert that the given list of [locations] has a [Location] corresponding | |
345 * to the [element]. | |
346 */ | |
347 void _assertHasDefinedName(List<Location> locations, Element element) { | |
348 String libraryUri = element.library.source.uri.toString(); | |
349 String unitUri = element.source.uri.toString(); | |
350 for (Location location in locations) { | |
351 if (location.libraryUri == libraryUri && | |
352 location.unitUri == unitUri && | |
353 location.offset == element.nameOffset && | |
354 location.length == element.nameLength) { | |
355 return; | |
356 } | |
357 } | |
358 fail('No declaration of $element at ${element.nameOffset} in\n' | |
359 '${locations.join('\n')}'); | |
360 } | |
361 | |
362 Future<Null> _indexTestUnit(String code, {bool declOnly: false}) async { | |
363 await resolveTestUnit(code); | |
364 if (declOnly) { | |
365 index.indexDeclarations(testUnit); | |
366 } else { | |
367 index.indexUnit(testUnit); | |
368 } | |
369 } | |
370 | |
371 Future<Source> _indexUnit(String path, String code) async { | |
372 Source source = addSource(path, code); | |
373 CompilationUnit unit = await resolveLibraryUnit(source); | |
374 index.indexUnit(unit); | |
375 return source; | |
376 } | |
377 } | |
378 | |
379 class _CompilationUnitElementMock extends TypedMock | |
380 implements CompilationUnitElement {} | |
OLD | NEW |