blob: de8f51e9a13ac2d2a3ca1330d92726ebaba6cb73 [file] [log] [blame]
// Copyright (c) 2014, 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/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/domain_analysis.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../mocks.dart';
import 'notification_navigation_test.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(GetNavigationTest);
});
}
@reflectiveTest
class GetNavigationTest extends AbstractNavigationTest {
static const String requestId = 'test-getNavigation';
@override
void setUp() {
super.setUp();
server.handlers = [
AnalysisDomainHandler(server),
];
createProject();
}
Future<void> test_beforeAnalysisComplete() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await _getNavigation(testFile, testCode.indexOf('test);'), 0);
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_comment_outsideReference() async {
addTestFile('''
/// Returns a [String].
String main() {
}''');
await waitForTasksFinished();
var search = 'Returns';
await _getNavigation(testFile, testCode.indexOf(search), 1);
expect(regions, hasLength(0));
}
Future<void> test_comment_reference() async {
addTestFile('''
/// Returns a [String].
String main() {
}''');
await waitForTasksFinished();
var search = '[String';
await _getNavigation(testFile, testCode.indexOf(search), 1);
expect(regions, hasLength(1));
assertHasRegion('String]');
}
Future<void> test_constructorInvocation() async {
// Check that a constructor invocation navigates to the constructor and not
// the class.
// https://github.com/dart-lang/sdk/issues/46725
addTestFile('''
class Foo {
// ...
// ...
Foo() {
print('');
}
// ...
}
final a = Foo();
final b = new Foo();
''');
await waitForTasksFinished();
// Without `new`
await _getNavigation(testFile, testCode.indexOf('Foo();'), 0);
expect(regions, hasLength(1));
expect(regions.first.targets, hasLength(1));
var target = targets[regions.first.targets.first];
expect(target.kind, ElementKind.CONSTRUCTOR);
expect(target.offset, testCode.indexOf('Foo() {'));
// With `new`
await _getNavigation(testFile, testCode.indexOf('new Foo();') + 4, 0);
expect(regions, hasLength(1));
expect(regions.first.targets, hasLength(1));
target = targets[regions.first.targets.first];
expect(target.kind, ElementKind.CONSTRUCTOR);
expect(target.offset, testCode.indexOf('Foo() {'));
}
Future<void> test_fieldType() async {
// This test mirrors test_navigation() from
// test/integration/analysis/get_navigation_test.dart
var text = r'''
class Foo {}
class Bar {
Foo foo;
}
''';
addTestFile(text);
await _getNavigation(testFile, text.indexOf('Foo foo'), 0);
expect(targets, hasLength(1));
var target = targets.first;
expect(target.kind, ElementKind.CLASS);
expect(target.offset, text.indexOf('Foo {'));
expect(target.length, 3);
expect(target.startLine, 1);
expect(target.startColumn, 7);
}
Future<void> test_fileDoesNotExist() async {
var file = convertPath('$projectPath/doesNotExist.dart');
var request = _createGetNavigationRequest(file, 0, 100);
var response = await serverChannel.sendRequest(request);
expect(response.error, isNull);
var result = response.result!;
expect(result['files'], isEmpty);
expect(result['targets'], isEmpty);
expect(result['regions'], isEmpty);
}
Future<void> test_fileOutsideOfRoot() async {
testFile = convertPath('/outside.dart');
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await _getNavigation(testFile, testCode.indexOf('test);'), 0);
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_importDirective() async {
addTestFile('''
import 'dart:math';
main() {
}''');
await waitForTasksFinished();
await _getNavigation(testFile, 0, 17);
expect(regions, hasLength(1));
assertHasRegionString("'dart:math'");
expect(testTargets, hasLength(1));
expect(testTargets[0].kind, ElementKind.LIBRARY);
}
Future<void> test_importUri() async {
addTestFile('''
import 'dart:math';
main() {
}''');
await waitForTasksFinished();
await _getNavigation(testFile, 7, 11);
expect(regions, hasLength(1));
assertHasRegionString("'dart:math'");
expect(testTargets, hasLength(1));
expect(testTargets[0].kind, ElementKind.LIBRARY);
}
Future<void> test_importUri_configurations() async {
final ioFile = newFile(join(testFolder, 'io.dart'));
final htmlFile = newFile(join(testFolder, 'html.dart'));
addTestFile('''
import 'foo.dart'
if (dart.library.io) 'io.dart'
if (dart.library.html) 'html.dart';
main() {
}''');
await waitForTasksFinished();
// Request navigations for 'io.dart'
await _getNavigation(testFile, 41, 9);
expect(regions, hasLength(1));
assertHasRegionString("'io.dart'");
expect(testTargets, hasLength(1));
var target = testTargets.first;
expect(target.kind, ElementKind.LIBRARY);
expect(targetFiles[target.fileIndex], equals(ioFile.path));
// Request navigations for 'html.dart'
await _getNavigation(testFile, 76, 11);
expect(regions, hasLength(1));
assertHasRegionString("'html.dart'");
expect(testTargets, hasLength(1));
target = testTargets.first;
expect(target.kind, ElementKind.LIBRARY);
expect(targetFiles[target.fileIndex], equals(htmlFile.path));
}
Future<void> test_invalidFilePathFormat_notAbsolute() async {
var request = _createGetNavigationRequest('test.dart', 0, 0);
var response = await waitResponse(request);
expect(
response,
isResponseFailure(requestId, RequestErrorCode.INVALID_FILE_PATH_FORMAT),
);
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
var request =
_createGetNavigationRequest(convertPath('/foo/../bar/test.dart'), 0, 0);
var response = await waitResponse(request);
expect(
response,
isResponseFailure(requestId, RequestErrorCode.INVALID_FILE_PATH_FORMAT),
);
}
Future<void> test_multipleRegions() async {
addTestFile('''
main() {
var aaa = 1;
var bbb = 2;
var ccc = 3;
var ddd = 4;
print(aaa + bbb + ccc + ddd);
}
''');
await waitForTasksFinished();
// request navigation
var navCode = ' + bbb + ';
await _getNavigation(testFile, testCode.indexOf(navCode), navCode.length);
// verify
{
assertHasRegion('aaa +');
assertHasTarget('aaa = 1');
}
{
assertHasRegion('bbb +');
assertHasTarget('bbb = 2');
}
{
assertHasRegion('ccc +');
assertHasTarget('ccc = 3');
}
assertNoRegionAt('ddd)');
}
Future<void> test_operator_index() async {
addTestFile('''
class A {
operator [](index) => 0;
operator []=(index, int value) {}
}
void f(A a) {
a[0]; // []
a[1] = 1; // []=
a[2] += 2;
}
''');
await waitForTasksFinished();
{
var search = '[0';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = ']; // []';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = '[1';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] = 1';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '[2';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] += 2';
await _getNavigation(testFile, testCode.indexOf(search), 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
}
Future<void> test_zeroLength_end() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await waitForTasksFinished();
await _getNavigation(testFile, testCode.indexOf(');'), 0);
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_zeroLength_start() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await waitForTasksFinished();
await _getNavigation(testFile, testCode.indexOf('test);'), 0);
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Request _createGetNavigationRequest(String file, int offset, int length) {
return AnalysisGetNavigationParams(file, offset, length)
.toRequest(requestId);
}
Future<void> _getNavigation(String file, int offset, int length) async {
var request = _createGetNavigationRequest(file, offset, length);
var response = await serverChannel.sendRequest(request);
var result = AnalysisGetNavigationResult.fromResponse(response);
targetFiles = result.files;
targets = result.targets;
regions = result.regions;
}
}