// 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_generated.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../support/integration_tests.dart'; void main() { defineReflectiveSuite(() { defineReflectiveTests(AnalysisNavigationTest); }); } @reflectiveTest class AnalysisNavigationTest extends AbstractAnalysisServerIntegrationTest { Future test_navigation() async { var pathname1 = sourcePath('test1.dart'); var text1 = r''' library foo; import 'dart:async'; part 'test2.dart'; class Class { Class.constructor(); /* constructor declaration */ TypeParameter field = (throw 0); method() {} } typedef FunctionTypeAlias(); function(FunctionTypeAlias parameter) { print(parameter()); } int topLevelVariable = 0; main() { Class localVariable = new Class.constructor(); // usage function(() => localVariable.field); localVariable.method(); localVariable.field = 1; } '''; writeFile(pathname1, text1); var pathname2 = sourcePath('test2.dart'); var text2 = r''' part of foo; '''; writeFile(pathname2, text2); standardAnalysisSetup(); sendAnalysisSetSubscriptions({ AnalysisService.NAVIGATION: [pathname1] }); // There should be a single error, due to the fact that 'dart:async' is not // used. await analysisFinished; expect(currentAnalysisErrors[pathname1], hasLength(1)); expect(currentAnalysisErrors[pathname2], isEmpty); var params = await onAnalysisNavigation.first; expect(params.file, equals(pathname1)); var regions = params.regions; var targets = params.targets; var targetFiles = params.files; NavigationTarget findTargetElement(int index) { for (var region in regions) { if (region.offset <= index && index < region.offset + region.length) { expect(region.targets, hasLength(1)); var targetIndex = region.targets[0]; return targets[targetIndex]; } } fail('No element found for index $index'); } void checkLocal( String source, String expectedTarget, ElementKind expectedKind) { var sourceIndex = text1.indexOf(source); var targetIndex = text1.indexOf(expectedTarget); var element = findTargetElement(sourceIndex); expect(targetFiles[element.fileIndex], equals(pathname1)); expect(element.offset, equals(targetIndex)); expect(element.kind, equals(expectedKind)); } void checkRemote( String source, String expectedTargetRegexp, ElementKind expectedKind) { var sourceIndex = text1.indexOf(source); var element = findTargetElement(sourceIndex); expect(targetFiles[element.fileIndex], matches(expectedTargetRegexp)); expect(element.kind, equals(expectedKind)); } // TODO(paulberry): will the element type 'CLASS_TYPE_ALIAS' ever appear as // a navigation target? checkLocal('Class', 'Class', ElementKind.CLASS); checkRemote("'test2.dart';", r'test2.dart$', ElementKind.COMPILATION_UNIT); checkLocal( 'Class.constructor', 'constructor(); /* constructor declaration */', ElementKind.CONSTRUCTOR); checkLocal( 'constructor(); // usage', 'constructor(); /* constructor declaration */', ElementKind.CONSTRUCTOR); checkLocal('field = (', 'field = (', ElementKind.FIELD); checkLocal('function(() => localVariable.field)', 'function(FunctionTypeAlias parameter)', ElementKind.FUNCTION); checkLocal('FunctionTypeAlias parameter', 'FunctionTypeAlias();', ElementKind.TYPE_ALIAS); checkLocal('field)', 'field = (', ElementKind.GETTER); checkRemote("'dart:async'", r'async\.dart$', ElementKind.LIBRARY); checkLocal( 'localVariable.field', 'localVariable =', ElementKind.LOCAL_VARIABLE); checkLocal('method();', 'method() {', ElementKind.METHOD); checkLocal('parameter());', 'parameter) {', ElementKind.PARAMETER); checkLocal('field = 1', 'field = (', ElementKind.SETTER); checkLocal('topLevelVariable = 0;', 'topLevelVariable = 0;', ElementKind.TOP_LEVEL_VARIABLE); checkLocal('TypeParameter field = (', 'TypeParameter>', ElementKind.TYPE_PARAMETER); } }