blob: 2d78865bb40aea65d2ec56c251d043207aba36a0 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:typed_mock/typed_mock.dart';
import '../../abstract_single_unit.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(IndexTest);
});
}
@reflectiveTest
class IndexTest extends AbstractSingleUnitTest {
Index index = createMemoryIndex();
/**
* Return the [Location] with given properties, or fail.
*/
Location findLocation(List<Location> locations, String libraryUri,
String unitUri, int offset, int length, bool isQualified) {
for (Location location in locations) {
if (location.libraryUri == libraryUri &&
location.unitUri == unitUri &&
location.offset == offset &&
location.length == length &&
location.isQualified == isQualified) {
return location;
}
}
fail('No at $offset with length $length qualified=$isQualified in\n'
'${locations.join('\n')}');
return null;
}
/**
* Return the [Location] with given properties, or fail.
*/
Location findLocationSource(
List<Location> locations, Source source, String search, bool isQualified,
{int length}) {
String code = source.contents.data;
int offset = code.indexOf(search);
expect(offset, isNonNegative, reason: 'Not found "$search" in\n$code');
length ??= getLeadingIdentifierLength(search);
String uri = source.uri.toString();
return findLocation(locations, uri, uri, offset, length, isQualified);
}
/**
* Return the [Location] with given properties, or fail.
*/
Location findLocationTest(
List<Location> locations, String search, bool isQualified,
{int length}) {
int offset = findOffset(search);
length ??= getLeadingIdentifierLength(search);
String testUri = testSource.uri.toString();
return findLocation(
locations, testUri, testUri, offset, length, isQualified);
}
void setUp() {
super.setUp();
}
void tearDown() {
super.tearDown();
index = null;
}
test_getDefinedNames_classMember() async {
_indexTestUnit('''
class A {
test() {}
}
class B {
int test = 1;
main() {
int test = 2;
}
}
''');
ClassElement classA = findElement('A');
ClassElement classB = findElement('B');
List<Location> locations = await index.getDefinedNames(
new RegExp(r'^test$'), IndexNameKind.classMember);
expect(locations, hasLength(2));
_assertHasDefinedName(locations, classA.methods[0]);
_assertHasDefinedName(locations, classB.fields[0]);
}
test_getDefinedNames_topLevel() async {
_indexTestUnit('''
class A {} // A
class B = Object with A;
typedef C();
D() {}
var E = null;
class NoMatchABCDE {}
''');
Element topA = findElement('A');
Element topB = findElement('B');
Element topC = findElement('C');
Element topD = findElement('D');
Element topE = findElement('E');
List<Location> locations = await index.getDefinedNames(
new RegExp(r'^[A-E]$'), IndexNameKind.topLevel);
expect(locations, hasLength(5));
_assertHasDefinedName(locations, topA);
_assertHasDefinedName(locations, topB);
_assertHasDefinedName(locations, topC);
_assertHasDefinedName(locations, topD);
_assertHasDefinedName(locations, topE);
}
test_getDefinedNames_topLevel2() async {
_indexTestUnit(
'''
class A {} // A
class B = Object with A;
class NoMatchABCDE {}
''',
declOnly: true);
Element topA = findElement('A');
Element topB = findElement('B');
List<Location> locations = await index.getDefinedNames(
new RegExp(r'^[A-E]$'), IndexNameKind.topLevel);
expect(locations, hasLength(2));
_assertHasDefinedName(locations, topA);
_assertHasDefinedName(locations, topB);
}
test_getRelations_isExtendedBy() async {
_indexTestUnit(r'''
class A {}
class B extends A {} // B
''');
Source source2 = _indexUnit(
'/test2.dart',
r'''
import 'test.dart';
class C extends A {} // C
''');
ClassElement elementA = testUnitElement.getType('A');
List<Location> locations =
await index.getRelations(elementA, IndexRelationKind.IS_EXTENDED_BY);
findLocationTest(locations, 'A {} // B', false);
findLocationSource(locations, source2, 'A {} // C', false);
}
test_getRelations_isReferencedBy() async {
_indexTestUnit(r'''
main(int a, int b) {
}
''');
ClassElement intElement = context.typeProvider.intType.element;
List<Location> locations = await index.getRelations(
intElement, IndexRelationKind.IS_REFERENCED_BY);
findLocationTest(locations, 'int a', false);
findLocationTest(locations, 'int b', false);
}
test_getUnresolvedMemberReferences_qualified_resolved() async {
_indexTestUnit('''
class A {
var test; // A
}
main(A a) {
print(a.test);
a.test = 1;
a.test += 2;
a.test();
}
''');
List<Location> locations =
await index.getUnresolvedMemberReferences('test');
expect(locations, isEmpty);
}
test_getUnresolvedMemberReferences_qualified_unresolved() async {
_indexTestUnit('''
class A {
var test; // A
}
main(p) {
print(p.test);
p.test = 1;
p.test += 2;
p.test();
print(p.test2); // not requested
}
''');
List<Location> locations =
await index.getUnresolvedMemberReferences('test');
expect(locations, hasLength(4));
findLocationTest(locations, 'test);', true);
findLocationTest(locations, 'test = 1;', true);
findLocationTest(locations, 'test += 2;', true);
findLocationTest(locations, 'test();', true);
}
test_getUnresolvedMemberReferences_unqualified_resolved() async {
_indexTestUnit('''
class A {
var test;
m() {
print(test);
test = 1;
test += 2;
test();
}
}
''');
List<Location> locations =
await index.getUnresolvedMemberReferences('test');
expect(locations, isEmpty);
}
test_getUnresolvedMemberReferences_unqualified_unresolved() async {
verifyNoTestUnitErrors = false;
_indexTestUnit('''
class A {
m() {
print(test);
test = 1;
test += 2;
test();
print(test2); // not requested
}
}
''');
List<Location> locations =
await index.getUnresolvedMemberReferences('test');
expect(locations, hasLength(4));
findLocationTest(locations, 'test);', false);
findLocationTest(locations, 'test = 1;', false);
findLocationTest(locations, 'test += 2;', false);
findLocationTest(locations, 'test();', false);
}
test_indexDeclarations_afterIndexUnit() async {
resolveTestUnit('''
var a = 0;
var b = a + 1;
''');
index.indexUnit(testUnit);
TopLevelVariableElement a = findElement('a');
// We can find references.
{
List<Location> locations = await index.getRelations(
a.getter, IndexRelationKind.IS_REFERENCED_BY);
findLocationTest(locations, 'a + 1', false);
}
// Attempt to index just declarations - we still can find references.
index.indexDeclarations(testUnit);
{
List<Location> locations = await index.getRelations(
a.getter, IndexRelationKind.IS_REFERENCED_BY);
findLocationTest(locations, 'a + 1', false);
}
}
test_indexDeclarations_nullUnit() async {
index.indexDeclarations(null);
}
test_indexDeclarations_nullUnitElement() async {
resolveTestUnit('');
testUnit.element = null;
index.indexDeclarations(testUnit);
}
test_indexUnit_nullLibraryElement() async {
resolveTestUnit('');
CompilationUnitElement unitElement = new _CompilationUnitElementMock();
expect(unitElement.library, isNull);
testUnit.element = unitElement;
index.indexUnit(testUnit);
}
test_indexUnit_nullUnit() async {
index.indexUnit(null);
}
test_indexUnit_nullUnitElement() async {
resolveTestUnit('');
testUnit.element = null;
index.indexUnit(testUnit);
}
test_removeContext() async {
_indexTestUnit('''
class A {}
''');
RegExp regExp = new RegExp(r'^A$');
expect(await index.getDefinedNames(regExp, IndexNameKind.topLevel),
hasLength(1));
// remove the context - no top-level declarations
index.removeContext(context);
expect(
await index.getDefinedNames(regExp, IndexNameKind.topLevel), isEmpty);
}
test_removeUnit() async {
RegExp regExp = new RegExp(r'^[AB]$');
Source sourceA = addSource('/a.dart', 'class A {}');
Source sourceB = addSource('/b.dart', 'class B {}');
CompilationUnit unitA = resolveLibraryUnit(sourceA);
CompilationUnit unitB = resolveLibraryUnit(sourceB);
index.indexUnit(unitA);
index.indexUnit(unitB);
{
List<Location> locations =
await index.getDefinedNames(regExp, IndexNameKind.topLevel);
expect(locations, hasLength(2));
expect(locations.map((l) => l.libraryUri),
unorderedEquals([sourceA.uri.toString(), sourceB.uri.toString()]));
}
// remove a.dart - no a.dart location
index.removeUnit(context, sourceA, sourceA);
{
List<Location> locations =
await index.getDefinedNames(regExp, IndexNameKind.topLevel);
expect(locations, hasLength(1));
expect(locations.map((l) => l.libraryUri),
unorderedEquals([sourceB.uri.toString()]));
}
}
/**
* Assert that the given list of [locations] has a [Location] corresponding
* to the [element].
*/
void _assertHasDefinedName(List<Location> locations, Element element) {
String libraryUri = element.library.source.uri.toString();
String unitUri = element.source.uri.toString();
for (Location location in locations) {
if (location.libraryUri == libraryUri &&
location.unitUri == unitUri &&
location.offset == element.nameOffset &&
location.length == element.nameLength) {
return;
}
}
fail('No declaration of $element at ${element.nameOffset} in\n'
'${locations.join('\n')}');
}
void _indexTestUnit(String code, {bool declOnly: false}) {
resolveTestUnit(code);
if (declOnly) {
index.indexDeclarations(testUnit);
} else {
index.indexUnit(testUnit);
}
}
Source _indexUnit(String path, String code) {
Source source = addSource(path, code);
CompilationUnit unit = resolveLibraryUnit(source);
index.indexUnit(unit);
return source;
}
}
class _CompilationUnitElementMock extends TypedMock
implements CompilationUnitElement {}