blob: 9927cead0da45887caf81f7a40d47d63a98c69c7 [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:analyzer/file_system/file_system.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'notification_navigation_test.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(GetNavigationTest);
});
}
@reflectiveTest
class GetNavigationTest extends AbstractNavigationTest {
static const String requestId = 'test-getNavigation';
@override
Future<void> setUp() async {
super.setUp();
await setRoots(included: [workspaceRootPath], excluded: []);
}
Future<void> test_beforeAnalysisComplete() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await _getNavigation(search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_comment_outsideReference() async {
addTestFile('''
/// Returns a [String].
String main() {
}''');
await waitForTasksFinished();
await _getNavigation(search: 'Returns', length: 1);
expect(regions, hasLength(0));
}
Future<void> test_comment_reference() async {
addTestFile('''
/// Returns a [String].
String main() {
}''');
await waitForTasksFinished();
await _getNavigation(search: '[String', length: 1);
expect(regions, hasLength(1));
assertHasRegion('String]');
}
Future<void> test_comment_toolSeeCodeComment() async {
var examplePath = 'examples/api/foo.dart';
newFile2('$testPackageLibPath/$examplePath', '');
addTestFile('''
/// {@tool dartpad}
/// ** See code in $examplePath **
/// {@end-tool}
String main() {
}''');
await waitForTasksFinished();
await _getNavigation(search: examplePath, length: 1);
expect(regions, hasLength(1));
assertHasRegion(examplePath, examplePath.length);
}
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() {}
Foo.named() {}
// ...
}
final a = Foo();
final b = new Foo.named(); // 0
''');
await waitForTasksFinished();
// Without `new` / unnamed
await _getNavigation(search: 'Foo();');
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, findOffset('Foo() {'));
expect(target.length, 3);
// With `new` / named
await _getNavigation(search: 'named(); // 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, findOffset('named() {'));
expect(target.length, 5);
}
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(search: 'Foo foo');
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('$testPackageLibPath/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);
}
/// TODO(scheglov) Rewrite these tests to work with any file.
@FailingTest(reason: 'requires infrastructure rewriting')
Future<void> test_fileOutsideOfRoot() async {
var file = newFile2('/outside.dart', '''
main() {
var test = 0;
print(test);
}
''');
await _getNavigation(file: file, search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_importDirective() async {
addTestFile('''
import 'dart:math';
main() {
}''');
await waitForTasksFinished();
await _getNavigation(offset: 0, length: 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(offset: 7, length: 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 = newFile2('$testPackageLibPath/io.dart', '');
final htmlFile = newFile2('$testPackageLibPath/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(offset: 41, length: 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(offset: 76, length: 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 handleRequest(request);
assertResponseFailure(
response,
requestId: requestId,
errorCode: RequestErrorCode.INVALID_FILE_PATH_FORMAT,
);
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
var request =
_createGetNavigationRequest(convertPath('/foo/../bar/test.dart'), 0, 0);
var response = await handleRequest(request);
assertResponseFailure(
response,
requestId: requestId,
errorCode: 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(search: navCode, length: 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(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = ']; // []';
await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[](index)', 2);
}
{
var search = '[1';
await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] = 1';
await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '[2';
await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
{
var search = '] += 2';
await _getNavigation(search: search, length: 1);
assertHasOperatorRegion(search, 1, '[]=(index', 3);
}
}
Future<void> test_zeroLength_end() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await waitForTasksFinished();
await _getNavigation(search: ');');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Future<void> test_zeroLength_start() async {
addTestFile('''
main() {
var test = 0;
print(test);
}
''');
await waitForTasksFinished();
await _getNavigation(search: 'test);');
assertHasRegion('test);');
assertHasTarget('test = 0');
}
Request _createGetNavigationRequest(String file, int offset, int length) {
return AnalysisGetNavigationParams(file, offset, length)
.toRequest(requestId);
}
Future<void> _getNavigation({
File? file,
int? offset,
String? search,
int length = 0,
}) async {
file ??= testFile;
if (offset == null) {
if (search != null) {
offset = offsetInFile(file, search);
} else {
throw ArgumentError("Either 'offset' or 'search' must be provided");
}
}
var request = _createGetNavigationRequest(file.path, offset, length);
var response = await serverChannel.sendRequest(request);
var result = AnalysisGetNavigationResult.fromResponse(response);
targetFiles = result.files;
targets = result.targets;
regions = result.regions;
}
}