01e0df71a8
Change-Id: Ib557755916b09be4e1ad1b1c2e4bed052a280513 Reviewed-on: https://dart-review.googlesource.com/c/84640 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
506 lines
14 KiB
Dart
506 lines
14 KiB
Dart
// 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 'dart:async';
|
|
|
|
import 'package:analysis_server/src/services/search/search_engine.dart';
|
|
import 'package:analysis_server/src/services/search/search_engine_internal.dart';
|
|
import 'package:analyzer/dart/element/element.dart';
|
|
import 'package:analyzer/src/dart/analysis/byte_store.dart';
|
|
import 'package:analyzer/src/dart/analysis/driver.dart';
|
|
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
|
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
|
|
import 'package:analyzer/src/file_system/file_system.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer/src/generated/sdk.dart';
|
|
import 'package:analyzer/src/generated/source.dart';
|
|
import 'package:analyzer/src/source/package_map_resolver.dart';
|
|
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
|
|
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(SearchEngineImplTest);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class SearchEngineImplTest with ResourceProviderMixin {
|
|
DartSdk sdk;
|
|
final ByteStore byteStore = new MemoryByteStore();
|
|
final FileContentOverlay contentOverlay = new FileContentOverlay();
|
|
|
|
final StringBuffer logBuffer = new StringBuffer();
|
|
PerformanceLog logger;
|
|
|
|
AnalysisDriverScheduler scheduler;
|
|
|
|
void setUp() {
|
|
sdk = new MockSdk(resourceProvider: resourceProvider);
|
|
logger = new PerformanceLog(logBuffer);
|
|
scheduler = new AnalysisDriverScheduler(logger);
|
|
scheduler.start();
|
|
}
|
|
|
|
test_membersOfSubtypes_hasMembers() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {
|
|
void a() {}
|
|
void b() {}
|
|
void c() {}
|
|
}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
class B extends A {
|
|
void a() {}
|
|
}
|
|
''').path;
|
|
var c = newFile('/test/c.dart', content: '''
|
|
import 'a.dart';
|
|
class C extends A {
|
|
void b() {}
|
|
}
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
driver2.addFile(c);
|
|
await scheduler.waitForIdle();
|
|
|
|
var resultA = await driver1.getResult(a);
|
|
ClassElement elementA = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
Set<String> members = await searchEngine.membersOfSubtypes(elementA);
|
|
expect(members, unorderedEquals(['a', 'b']));
|
|
}
|
|
|
|
test_membersOfSubtypes_noMembers() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {
|
|
void a() {}
|
|
void b() {}
|
|
void c() {}
|
|
}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
class B extends A {}
|
|
''').path;
|
|
|
|
var driver = _newDriver();
|
|
|
|
driver.addFile(a);
|
|
driver.addFile(b);
|
|
await scheduler.waitForIdle();
|
|
|
|
var resultA = await driver.getResult(a);
|
|
ClassElement elementA = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver]);
|
|
Set<String> members = await searchEngine.membersOfSubtypes(elementA);
|
|
expect(members, isEmpty);
|
|
}
|
|
|
|
test_membersOfSubtypes_noSubtypes() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {
|
|
void a() {}
|
|
void b() {}
|
|
void c() {}
|
|
}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
class B {
|
|
void a() {}
|
|
}
|
|
''').path;
|
|
|
|
var driver = _newDriver();
|
|
|
|
driver.addFile(a);
|
|
driver.addFile(b);
|
|
await scheduler.waitForIdle();
|
|
|
|
var resultA = await driver.getResult(a);
|
|
ClassElement elementA = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver]);
|
|
Set<String> members = await searchEngine.membersOfSubtypes(elementA);
|
|
expect(members, isNull);
|
|
}
|
|
|
|
test_membersOfSubtypes_private() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {
|
|
void a() {}
|
|
void _b() {}
|
|
void _c() {}
|
|
}
|
|
class B extends A {
|
|
void _b() {}
|
|
}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
class C extends A {
|
|
void a() {}
|
|
void _c() {}
|
|
}
|
|
class D extends B {
|
|
void _c() {}
|
|
}
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
await scheduler.waitForIdle();
|
|
|
|
var resultA = await driver1.getResult(a);
|
|
ClassElement elementA = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
Set<String> members = await searchEngine.membersOfSubtypes(elementA);
|
|
expect(members, unorderedEquals(['a', '_b']));
|
|
}
|
|
|
|
test_searchAllSubtypes() async {
|
|
var p = newFile('/test.dart', content: '''
|
|
class T {}
|
|
class A extends T {}
|
|
class B extends A {}
|
|
class C implements B {}
|
|
''').path;
|
|
|
|
var driver = _newDriver();
|
|
driver.addFile(p);
|
|
|
|
var resultA = await driver.getResult(p);
|
|
ClassElement element = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver]);
|
|
Set<ClassElement> subtypes = await searchEngine.searchAllSubtypes(element);
|
|
expect(subtypes, hasLength(3));
|
|
_assertContainsClass(subtypes, 'A');
|
|
_assertContainsClass(subtypes, 'B');
|
|
_assertContainsClass(subtypes, 'C');
|
|
}
|
|
|
|
test_searchAllSubtypes_acrossDrivers() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class T {}
|
|
class A extends T {}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
class B extends A {}
|
|
class C extends B {}
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
|
|
var resultA = await driver1.getResult(a);
|
|
ClassElement element = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
Set<ClassElement> subtypes = await searchEngine.searchAllSubtypes(element);
|
|
expect(subtypes, hasLength(3));
|
|
expect(subtypes, contains(predicate((ClassElement e) => e.name == 'A')));
|
|
expect(subtypes, contains(predicate((ClassElement e) => e.name == 'B')));
|
|
expect(subtypes, contains(predicate((ClassElement e) => e.name == 'C')));
|
|
}
|
|
|
|
test_searchAllSubtypes_mixin() async {
|
|
var p = newFile('/test.dart', content: '''
|
|
class T {}
|
|
|
|
mixin A on T {}
|
|
mixin B implements T {}
|
|
|
|
class C extends T {}
|
|
|
|
mixin D on C {}
|
|
mixin E implements C {}
|
|
''').path;
|
|
|
|
var driver = _newDriver();
|
|
driver.addFile(p);
|
|
|
|
var resultA = await driver.getResult(p);
|
|
ClassElement element = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver]);
|
|
Set<ClassElement> subtypes = await searchEngine.searchAllSubtypes(element);
|
|
expect(subtypes, hasLength(5));
|
|
_assertContainsClass(subtypes, 'A');
|
|
_assertContainsClass(subtypes, 'B');
|
|
_assertContainsClass(subtypes, 'C');
|
|
_assertContainsClass(subtypes, 'D');
|
|
_assertContainsClass(subtypes, 'E');
|
|
}
|
|
|
|
test_searchMemberDeclarations() async {
|
|
var codeA = '''
|
|
class A {
|
|
int test; // 1
|
|
int testTwo;
|
|
}
|
|
''';
|
|
var codeB = '''
|
|
class B {
|
|
void test() {} // 2
|
|
void testTwo() {}
|
|
}
|
|
int test;
|
|
''';
|
|
|
|
var a = newFile('/test/a.dart', content: codeA).path;
|
|
var b = newFile('/test/b.dart', content: codeB).path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
|
|
while (scheduler.isAnalyzing) {
|
|
await new Future.delayed(new Duration(milliseconds: 1));
|
|
}
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
List<SearchMatch> matches =
|
|
await searchEngine.searchMemberDeclarations('test');
|
|
expect(matches, hasLength(2));
|
|
|
|
void assertHasElement(String name, int nameOffset) {
|
|
expect(
|
|
matches,
|
|
contains(predicate((SearchMatch m) =>
|
|
m.kind == MatchKind.DECLARATION &&
|
|
m.element.name == name &&
|
|
m.element.nameOffset == nameOffset)));
|
|
}
|
|
|
|
assertHasElement('test', codeA.indexOf('test; // 1'));
|
|
assertHasElement('test', codeB.indexOf('test() {} // 2'));
|
|
}
|
|
|
|
test_searchMemberReferences() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {
|
|
int test;
|
|
}
|
|
foo(p) {
|
|
p.test;
|
|
}
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
bar(p) {
|
|
p.test = 1;
|
|
}
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
List<SearchMatch> matches =
|
|
await searchEngine.searchMemberReferences('test');
|
|
expect(matches, hasLength(2));
|
|
expect(
|
|
matches,
|
|
contains(predicate((SearchMatch m) =>
|
|
m.element.name == 'foo' || m.kind == MatchKind.READ)));
|
|
expect(
|
|
matches,
|
|
contains(predicate((SearchMatch m) =>
|
|
m.element.name == 'bar' || m.kind == MatchKind.WRITE)));
|
|
}
|
|
|
|
test_searchReferences() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class T {}
|
|
T a;
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
import 'a.dart';
|
|
T b;
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
|
|
var resultA = await driver1.getResult(a);
|
|
ClassElement element = resultA.unit.declaredElement.types[0];
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
List<SearchMatch> matches = await searchEngine.searchReferences(element);
|
|
expect(matches, hasLength(2));
|
|
expect(
|
|
matches, contains(predicate((SearchMatch m) => m.element.name == 'a')));
|
|
expect(
|
|
matches, contains(predicate((SearchMatch m) => m.element.name == 'b')));
|
|
}
|
|
|
|
test_searchReferences_discover_owned() async {
|
|
var t = newFile('/test/lib/t.dart', content: '''
|
|
import 'package:aaa/a.dart';
|
|
int t;
|
|
''').path;
|
|
var a = newFile('/aaa/lib/a.dart', content: '''
|
|
int a;
|
|
''').path;
|
|
|
|
var driver1 = _newDriver(packageUriResolver: _mapUriResolver('aaa', a));
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(t);
|
|
driver2.addFile(a);
|
|
|
|
LibraryElement coreLib = await driver1.getLibraryByUri('dart:core');
|
|
ClassElement intElement = coreLib.getType('int');
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
var matches = await searchEngine.searchReferences(intElement);
|
|
|
|
void assertHasOne(String path, String name) {
|
|
expect(matches.where((m) {
|
|
var element = m.element;
|
|
return element.name == name && element.source.fullName == path;
|
|
}), hasLength(1));
|
|
}
|
|
|
|
assertHasOne(t, 't');
|
|
assertHasOne(a, 'a');
|
|
}
|
|
|
|
test_searchTopLevelDeclarations() async {
|
|
var a = newFile('/test/a.dart', content: '''
|
|
class A {}
|
|
int a;
|
|
''').path;
|
|
var b = newFile('/test/b.dart', content: '''
|
|
class B {}
|
|
get b => 42;
|
|
''').path;
|
|
|
|
var driver1 = _newDriver();
|
|
var driver2 = _newDriver();
|
|
|
|
driver1.addFile(a);
|
|
driver2.addFile(b);
|
|
|
|
while (scheduler.isAnalyzing) {
|
|
await new Future.delayed(new Duration(milliseconds: 1));
|
|
}
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
var matches = await searchEngine.searchTopLevelDeclarations('.*');
|
|
matches.removeWhere((match) => match.libraryElement.isInSdk);
|
|
expect(matches, hasLength(4));
|
|
|
|
void assertHasOneElement(String name) {
|
|
Iterable<SearchMatch> nameMatches = matches.where((SearchMatch m) =>
|
|
m.kind == MatchKind.DECLARATION && m.element.name == name);
|
|
expect(nameMatches, hasLength(1));
|
|
}
|
|
|
|
assertHasOneElement('A');
|
|
assertHasOneElement('a');
|
|
assertHasOneElement('B');
|
|
assertHasOneElement('b');
|
|
}
|
|
|
|
test_searchTopLevelDeclarations_dependentPackage() async {
|
|
var a = newFile('/a/lib/a.dart', content: '''
|
|
class A {}
|
|
''').path;
|
|
var driver1 = _newDriver();
|
|
driver1.addFile(a);
|
|
|
|
// The package:b uses the class A from the package:a,
|
|
// so it sees the declaration the element A.
|
|
var b = newFile('/b/lib/b.dart', content: '''
|
|
import 'package:a/a.dart';
|
|
class B extends A {}
|
|
''');
|
|
var driver2 = _newDriver(packageUriResolver: _mapUriResolver('a', a));
|
|
driver2.addFile(b.path);
|
|
|
|
while (scheduler.isAnalyzing) {
|
|
await new Future.delayed(new Duration(milliseconds: 1));
|
|
}
|
|
|
|
var searchEngine = new SearchEngineImpl([driver1, driver2]);
|
|
List<SearchMatch> matches =
|
|
await searchEngine.searchTopLevelDeclarations('.*');
|
|
// We get exactly two items: A and B.
|
|
// I.e. we get exactly one A.
|
|
expect(
|
|
matches.where((match) => !match.libraryElement.isInSdk), hasLength(2));
|
|
|
|
void assertHasOneElement(String name) {
|
|
Iterable<SearchMatch> nameMatches = matches.where((SearchMatch m) =>
|
|
m.kind == MatchKind.DECLARATION && m.element.name == name);
|
|
expect(nameMatches, hasLength(1));
|
|
}
|
|
|
|
assertHasOneElement('A');
|
|
assertHasOneElement('B');
|
|
}
|
|
|
|
UriResolver _mapUriResolver(String packageName, String path) {
|
|
return new PackageMapUriResolver(resourceProvider, {
|
|
packageName: [resourceProvider.getFile(path).parent]
|
|
});
|
|
}
|
|
|
|
AnalysisDriver _newDriver({UriResolver packageUriResolver}) {
|
|
var resolvers = <UriResolver>[
|
|
new DartUriResolver(sdk),
|
|
new ResourceUriResolver(resourceProvider)
|
|
];
|
|
if (packageUriResolver != null) {
|
|
resolvers.add(packageUriResolver);
|
|
}
|
|
resolvers.add(new ResourceUriResolver(resourceProvider));
|
|
|
|
return new AnalysisDriver(
|
|
scheduler,
|
|
logger,
|
|
resourceProvider,
|
|
byteStore,
|
|
contentOverlay,
|
|
null,
|
|
new SourceFactory(resolvers, null, resourceProvider),
|
|
new AnalysisOptionsImpl(),
|
|
enableIndex: true);
|
|
}
|
|
|
|
static void _assertContainsClass(Set<ClassElement> subtypes, String name) {
|
|
expect(subtypes, contains(predicate((ClassElement e) => e.name == name)));
|
|
}
|
|
}
|