| // Copyright (c) 2020, 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/completion/dart/utilities.dart'; |
| import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; |
| import 'package:analyzer_plugin/protocol/protocol_common.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../services/completion/dart/completion_contributor_util.dart'; |
| import 'impl/completion_driver.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(BasicCompletionTest); |
| defineReflectiveTests(CompletionWithSuggestionsTest); |
| }); |
| } |
| |
| abstract class AbstractCompletionDriverTest with ResourceProviderMixin { |
| CompletionDriver driver; |
| Map<String, String> packageRoots; |
| List<CompletionSuggestion> suggestions; |
| |
| String get projectName => 'project'; |
| |
| String get projectPath => '/$projectName'; |
| |
| bool get supportsAvailableSuggestions; |
| |
| String get testFilePath => '$projectPath/bin/test.dart'; |
| |
| Future<void> addProjectFile(String relativePath, String content) async { |
| newFile('$projectPath/$relativePath', content: content); |
| // todo (pq): handle more than lib |
| expect(relativePath, startsWith('lib/')); |
| var packageRelativePath = relativePath.substring(4); |
| var uriStr = 'package:$projectName/$packageRelativePath'; |
| await driver.waitForSetWithUri(uriStr); |
| } |
| |
| Future<void> addTestFile(String content, {int offset}) async { |
| driver.addTestFile(content, offset: offset); |
| await getSuggestions(); |
| } |
| |
| Future<List<CompletionSuggestion>> getSuggestions() async { |
| if (supportsAvailableSuggestions) { |
| // todo (pq): consider moving |
| const internalLibs = [ |
| 'dart:async2', |
| 'dart:_interceptors', |
| 'dart:_internal', |
| ]; |
| for (var lib in driver.sdk.sdkLibraries) { |
| var uri = lib.shortName; |
| if (!internalLibs.contains(uri)) { |
| await driver.waitForSetWithUri(uri); |
| } |
| } |
| } |
| |
| suggestions = await driver.getSuggestions(); |
| return suggestions; |
| } |
| |
| /// Display sorted suggestions. |
| void printSuggestions() { |
| suggestions.sort(completionComparator); |
| for (var s in suggestions) { |
| print( |
| '[${s.relevance}] ${s.completion} • ${s.element?.kind?.name ?? ""} ${s.kind.name} ${s.element?.location?.file ?? ""}'); |
| } |
| } |
| |
| @mustCallSuper |
| void setUp() { |
| driver = CompletionDriver( |
| supportsAvailableSuggestions: supportsAvailableSuggestions, |
| projectPath: projectPath, |
| testFilePath: testFilePath, |
| resourceProvider: resourceProvider); |
| driver.createProject(packageRoots: packageRoots); |
| |
| newFile('$projectPath/pubspec.yaml', content: ''); |
| newFile('$projectPath/.packages', content: ''' |
| project:${toUri('$projectPath/lib')} |
| '''); |
| // todo (pq): add logic (possibly to driver) that waits for SDK suggestions |
| } |
| |
| SuggestionMatcher suggestionHas( |
| {@required String completion, |
| ElementKind element, |
| CompletionSuggestionKind kind}) => |
| (CompletionSuggestion s) { |
| if (s.completion == completion) { |
| if (element != null && s.element?.kind != element) { |
| return false; |
| } |
| if (kind != null && s.kind != kind) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| }; |
| |
| Iterable<CompletionSuggestion> suggestionsWith( |
| {@required String completion, |
| ElementKind element, |
| CompletionSuggestionKind kind}) => |
| suggestions.where( |
| suggestionHas(completion: completion, element: element, kind: kind)); |
| |
| CompletionSuggestion suggestionWith( |
| {@required String completion, |
| ElementKind element, |
| CompletionSuggestionKind kind}) { |
| final matches = |
| suggestionsWith(completion: completion, element: element, kind: kind); |
| expect(matches, hasLength(1)); |
| return matches.first; |
| } |
| } |
| |
| @reflectiveTest |
| class BasicCompletionTest extends AbstractCompletionDriverTest { |
| @override |
| bool get supportsAvailableSuggestions => false; |
| |
| /// Duplicates (and potentially replaces DeprecatedMemberRelevanceTest). |
| Future<void> test_deprecated_member_relevance() async { |
| await addTestFile(''' |
| class A { |
| void a1() { } |
| @deprecated |
| void a2() { } |
| } |
| |
| void main() { |
| var a = A(); |
| a.^ |
| } |
| '''); |
| |
| expect( |
| suggestionWith( |
| completion: 'a2', |
| element: ElementKind.METHOD, |
| kind: CompletionSuggestionKind.INVOCATION) |
| .relevance, |
| lessThan(suggestionWith( |
| completion: 'a1', |
| element: ElementKind.METHOD, |
| kind: CompletionSuggestionKind.INVOCATION) |
| .relevance)); |
| } |
| } |
| |
| @reflectiveTest |
| class CompletionWithSuggestionsTest extends AbstractCompletionDriverTest { |
| @override |
| bool get supportsAvailableSuggestions => true; |
| |
| @override |
| String get testFilePath => '$projectPath/lib/test.dart'; |
| |
| Future<void> test_basic() async { |
| await addProjectFile('lib/a.dart', r''' |
| class A {} |
| '''); |
| |
| await addTestFile(''' |
| void main() { |
| ^ |
| } |
| '''); |
| |
| var suggestions = suggestionsWith( |
| completion: 'A', kind: CompletionSuggestionKind.INVOCATION); |
| // todo (pq): seems like this should be 1; investigate duplication. |
| expect(suggestions, hasLength(2)); |
| } |
| |
| Future<void> test_sdk_lib_suggestions() async { |
| await addTestFile(''' |
| void main() { |
| ^ |
| } |
| '''); |
| |
| // A set of SDK suggestions. |
| expect( |
| // from dart:async (StreamSubscription) |
| suggestionWith( |
| completion: 'asFuture', kind: CompletionSuggestionKind.INVOCATION), |
| isNotNull); |
| |
| expect( |
| // from dart:core |
| suggestionWith( |
| completion: 'print', kind: CompletionSuggestionKind.INVOCATION), |
| isNotNull); |
| |
| expect( |
| // from dart:collection (ListMixin) |
| suggestionWith( |
| completion: 'firstWhere', |
| kind: CompletionSuggestionKind.INVOCATION), |
| isNotNull); |
| |
| expect( |
| // from dart:math |
| suggestionWith( |
| completion: 'tan', kind: CompletionSuggestionKind.INVOCATION), |
| isNotNull); |
| } |
| } |