Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Unified Diff: pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart

Issue 2927663002: Port two completion contributors for use by plugins (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
diff --git a/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart b/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
new file mode 100644
index 0000000000000000000000000000000000000000..24e115082c613dd8b1e28a349781b55ee8330a65
--- /dev/null
+++ b/pkg/analyzer_plugin/test/utilities/completion/completion_contributor_util.dart
@@ -0,0 +1,569 @@
+// Copyright (c) 2017, 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:analyzer/file_system/file_system.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:analyzer_plugin/src/utilities/completion/completion_core.dart';
+import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
+import 'package:analyzer_plugin/utilities/completion/completion_core.dart';
+import 'package:analyzer_plugin/utilities/completion/relevance.dart';
+import 'package:test/test.dart';
+
+import '../../support/abstract_context.dart';
+import 'flutter_util.dart';
+import 'replacement_range.dart';
+
+int suggestionComparator(CompletionSuggestion s1, CompletionSuggestion s2) {
+ String c1 = s1.completion.toLowerCase();
+ String c2 = s2.completion.toLowerCase();
+ return c1.compareTo(c2);
+}
+
+abstract class DartCompletionContributorTest extends AbstractContextTest {
+ static const String _UNCHECKED = '__UNCHECKED__';
+ String testFile = '/completionTest.dart';
+ Source testSource;
+ int completionOffset;
+ int replacementOffset;
+ int replacementLength;
+ CompletionContributor contributor;
+ CompletionRequest request;
+ List<CompletionSuggestion> suggestions;
+
+ /**
+ * If `true` and `null` is specified as the suggestion's expected returnType
+ * then the actual suggestion is expected to have a `dynamic` returnType.
+ * Newer tests return `false` so that they can distinguish between
+ * `dynamic` and `null`.
+ * Eventually all tests should be converted and this getter removed.
+ */
+ bool get isNullExpectedReturnTypeConsideredDynamic => true;
+
+ void addTestSource(String content) {
+ expect(completionOffset, isNull, reason: 'Call addTestUnit exactly once');
+ completionOffset = content.indexOf('^');
+ expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
+ int nextOffset = content.indexOf('^', completionOffset + 1);
+ expect(nextOffset, equals(-1), reason: 'too many ^');
+ content = content.substring(0, completionOffset) +
+ content.substring(completionOffset + 1);
+ testSource = addSource(testFile, content);
+ }
+
+ void assertHasNoParameterInfo(CompletionSuggestion suggestion) {
+ expect(suggestion.parameterNames, isNull);
+ expect(suggestion.parameterTypes, isNull);
+ expect(suggestion.requiredParameterCount, isNull);
+ expect(suggestion.hasNamedParameters, isNull);
+ }
+
+ void assertHasParameterInfo(CompletionSuggestion suggestion) {
+ expect(suggestion.parameterNames, isNotNull);
+ expect(suggestion.parameterTypes, isNotNull);
+ expect(suggestion.parameterNames.length, suggestion.parameterTypes.length);
+ expect(suggestion.requiredParameterCount,
+ lessThanOrEqualTo(suggestion.parameterNames.length));
+ expect(suggestion.hasNamedParameters, isNotNull);
+ }
+
+ void assertNoSuggestions({CompletionSuggestionKind kind: null}) {
+ if (kind == null) {
+ if (suggestions.length > 0) {
+ failedCompletion('Expected no suggestions', suggestions);
+ }
+ return;
+ }
+ CompletionSuggestion suggestion = suggestions.firstWhere(
+ (CompletionSuggestion cs) => cs.kind == kind,
+ orElse: () => null);
+ if (suggestion != null) {
+ failedCompletion('did not expect completion: $completion\n $suggestion');
+ }
+ }
+
+ void assertNotSuggested(String completion) {
+ CompletionSuggestion suggestion = suggestions.firstWhere(
+ (CompletionSuggestion cs) => cs.completion == completion,
+ orElse: () => null);
+ if (suggestion != null) {
+ failedCompletion('did not expect completion: $completion\n $suggestion');
+ }
+ }
+
+ CompletionSuggestion assertSuggest(String completion,
+ {CompletionSuggestionKind csKind: CompletionSuggestionKind.INVOCATION,
+ int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ ElementKind elemKind: null,
+ bool isDeprecated: false,
+ bool isPotential: false,
+ String elemFile,
+ int elemOffset,
+ int selectionOffset,
+ String paramName,
+ String paramType,
+ String defaultArgListString: _UNCHECKED,
+ List<int> defaultArgumentListTextRanges}) {
+ CompletionSuggestion cs =
+ getSuggest(completion: completion, csKind: csKind, elemKind: elemKind);
+ if (cs == null) {
+ failedCompletion('expected $completion $csKind $elemKind', suggestions);
+ }
+ expect(cs.kind, equals(csKind));
+ if (isDeprecated) {
+ expect(cs.relevance, equals(DART_RELEVANCE_LOW));
+ } else {
+ expect(cs.relevance, equals(relevance), reason: completion);
+ }
+ expect(cs.importUri, importUri);
+ expect(cs.selectionOffset, equals(selectionOffset ?? completion.length));
+ expect(cs.selectionLength, equals(0));
+ expect(cs.isDeprecated, equals(isDeprecated));
+ expect(cs.isPotential, equals(isPotential));
+ if (cs.element != null) {
+ expect(cs.element.location, isNotNull);
+ expect(cs.element.location.file, isNotNull);
+ expect(cs.element.location.offset, isNotNull);
+ expect(cs.element.location.length, isNotNull);
+ expect(cs.element.location.startColumn, isNotNull);
+ expect(cs.element.location.startLine, isNotNull);
+ }
+ if (elemFile != null) {
+ expect(cs.element.location.file, elemFile);
+ }
+ if (elemOffset != null) {
+ expect(cs.element.location.offset, elemOffset);
+ }
+ if (paramName != null) {
+ expect(cs.parameterName, paramName);
+ }
+ if (paramType != null) {
+ expect(cs.parameterType, paramType);
+ }
+ if (defaultArgListString != _UNCHECKED) {
+ expect(cs.defaultArgumentListString, defaultArgListString);
+ }
+ if (defaultArgumentListTextRanges != null) {
+ expect(cs.defaultArgumentListTextRanges, defaultArgumentListTextRanges);
+ }
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestClass(String name,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ bool isDeprecated: false,
+ String elemFile,
+ String elemName,
+ int elemOffset}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ isDeprecated: isDeprecated,
+ elemFile: elemFile,
+ elemOffset: elemOffset);
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.CLASS));
+ expect(element.name, equals(elemName ?? name));
+ expect(element.parameters, isNull);
+ expect(element.returnType, isNull);
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestClassTypeAlias(String name,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION}) {
+ CompletionSuggestion cs =
+ assertSuggest(name, csKind: kind, relevance: relevance);
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.CLASS_TYPE_ALIAS));
+ expect(element.name, equals(name));
+ expect(element.parameters, isNull);
+ expect(element.returnType, isNull);
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestConstructor(String name,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ int elemOffset,
+ String defaultArgListString: _UNCHECKED,
+ List<int> defaultArgumentListTextRanges}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ relevance: relevance,
+ importUri: importUri,
+ elemOffset: elemOffset,
+ defaultArgListString: defaultArgListString,
+ defaultArgumentListTextRanges: defaultArgumentListTextRanges);
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.CONSTRUCTOR));
+ int index = name.indexOf('.');
+ expect(element.name, index >= 0 ? name.substring(index + 1) : '');
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestEnum(String completion,
+ {bool isDeprecated: false}) {
+ CompletionSuggestion suggestion =
+ assertSuggest(completion, isDeprecated: isDeprecated);
+ expect(suggestion.isDeprecated, isDeprecated);
+ expect(suggestion.element.kind, ElementKind.ENUM);
+ return suggestion;
+ }
+
+ CompletionSuggestion assertSuggestEnumConst(String completion,
+ {int relevance: DART_RELEVANCE_DEFAULT, bool isDeprecated: false}) {
+ CompletionSuggestion suggestion = assertSuggest(completion,
+ relevance: relevance, isDeprecated: isDeprecated);
+ expect(suggestion.completion, completion);
+ expect(suggestion.isDeprecated, isDeprecated);
+ expect(suggestion.element.kind, ElementKind.ENUM_CONSTANT);
+ return suggestion;
+ }
+
+ CompletionSuggestion assertSuggestField(String name, String type,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ bool isDeprecated: false}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ elemKind: ElementKind.FIELD,
+ isDeprecated: isDeprecated);
+ // The returnType represents the type of a field
+ expect(cs.returnType, type != null ? type : 'dynamic');
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.FIELD));
+ expect(element.name, equals(name));
+ expect(element.parameters, isNull);
+ // The returnType represents the type of a field
+ expect(element.returnType, type != null ? type : 'dynamic');
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestFunction(String name, String returnType,
+ {CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ bool isDeprecated: false,
+ int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ String defaultArgListString: _UNCHECKED,
+ List<int> defaultArgumentListTextRanges}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ isDeprecated: isDeprecated,
+ defaultArgListString: defaultArgListString,
+ defaultArgumentListTextRanges: defaultArgumentListTextRanges);
+ if (returnType != null) {
+ expect(cs.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(cs.returnType, 'dynamic');
+ }
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.FUNCTION));
+ expect(element.name, equals(name));
+ expect(element.isDeprecated, equals(isDeprecated));
+ String param = element.parameters;
+ expect(param, isNotNull);
+ expect(param[0], equals('('));
+ expect(param[param.length - 1], equals(')'));
+ if (returnType != null) {
+ expect(element.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(element.returnType, 'dynamic');
+ }
+ assertHasParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestFunctionTypeAlias(
+ String name, String returnType,
+ {bool isDeprecated: false,
+ int relevance: DART_RELEVANCE_DEFAULT,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ String importUri}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ isDeprecated: isDeprecated);
+ if (returnType != null) {
+ expect(cs.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(cs.returnType, 'dynamic');
+ } else {
+ expect(cs.returnType, isNull);
+ }
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.FUNCTION_TYPE_ALIAS));
+ expect(element.name, equals(name));
+ expect(element.isDeprecated, equals(isDeprecated));
+ // TODO (danrubel) Determine why params are null
+ // String param = element.parameters;
+ // expect(param, isNotNull);
+ // expect(param[0], equals('('));
+ // expect(param[param.length - 1], equals(')'));
+ expect(element.returnType,
+ equals(returnType != null ? returnType : 'dynamic'));
+ // TODO (danrubel) Determine why param info is missing
+ // assertHasParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestGetter(String name, String returnType,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ bool isDeprecated: false}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ elemKind: ElementKind.GETTER,
+ isDeprecated: isDeprecated);
+ expect(cs.returnType, returnType != null ? returnType : 'dynamic');
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.GETTER));
+ expect(element.name, equals(name));
+ expect(element.parameters, isNull);
+ expect(element.returnType,
+ equals(returnType != null ? returnType : 'dynamic'));
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestMethod(
+ String name, String declaringType, String returnType,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ bool isDeprecated: false,
+ String defaultArgListString: _UNCHECKED,
+ List<int> defaultArgumentListTextRanges}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ isDeprecated: isDeprecated,
+ defaultArgListString: defaultArgListString,
+ defaultArgumentListTextRanges: defaultArgumentListTextRanges);
+ expect(cs.declaringType, equals(declaringType));
+ expect(cs.returnType, returnType != null ? returnType : 'dynamic');
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.METHOD));
+ expect(element.name, equals(name));
+ String param = element.parameters;
+ expect(param, isNotNull);
+ expect(param[0], equals('('));
+ expect(param[param.length - 1], equals(')'));
+ expect(element.returnType, returnType != null ? returnType : 'dynamic');
+ assertHasParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestName(String name,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.IDENTIFIER,
+ bool isDeprecated: false}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ isDeprecated: isDeprecated);
+ expect(cs.completion, equals(name));
+ expect(cs.element, isNull);
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestSetter(String name,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ String importUri,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind,
+ relevance: relevance,
+ importUri: importUri,
+ elemKind: ElementKind.SETTER);
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.SETTER));
+ expect(element.name, equals(name));
+ // TODO (danrubel) assert setter param
+ //expect(element.parameters, isNull);
+ // TODO (danrubel) it would be better if this was always null
+ if (element.returnType != null) {
+ expect(element.returnType, 'dynamic');
+ }
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ CompletionSuggestion assertSuggestTopLevelVar(String name, String returnType,
+ {int relevance: DART_RELEVANCE_DEFAULT,
+ CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION,
+ String importUri}) {
+ CompletionSuggestion cs = assertSuggest(name,
+ csKind: kind, relevance: relevance, importUri: importUri);
+ if (returnType != null) {
+ expect(cs.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(cs.returnType, 'dynamic');
+ }
+ Element element = cs.element;
+ expect(element, isNotNull);
+ expect(element.kind, equals(ElementKind.TOP_LEVEL_VARIABLE));
+ expect(element.name, equals(name));
+ expect(element.parameters, isNull);
+ if (returnType != null) {
+ expect(element.returnType, returnType);
+ } else if (isNullExpectedReturnTypeConsideredDynamic) {
+ expect(element.returnType, 'dynamic');
+ }
+ assertHasNoParameterInfo(cs);
+ return cs;
+ }
+
+ /**
+ * Return a [Future] that completes with the containing library information
+ * after it is accessible via [context.getLibrariesContaining].
+ */
+ Future<Null> computeLibrariesContaining() {
+ return driver.getResult(testFile).then((result) => null);
+ }
+
+ Future computeSuggestions() async {
+ AnalysisResult analysisResult = await driver.getResult(testFile);
+ testSource = analysisResult.unit.element.source;
+ request =
+ new CompletionRequestImpl(provider, analysisResult, completionOffset);
+
+ CompletionTarget target =
+ new CompletionTarget.forOffset(request.result.unit, request.offset);
+ var range = new ReplacementRange.compute(request.offset, target);
+ replacementOffset = range.offset;
+ replacementLength = range.length;
+
+ // Request completions
+ CompletionCollectorImpl collector = new CompletionCollectorImpl();
+ await contributor.computeSuggestions(request, collector);
+ suggestions = collector.suggestions;
+ expect(suggestions, isNotNull, reason: 'expected suggestions');
+ }
+
+ /**
+ * Configures the [SourceFactory] to have the `flutter` package in
+ * `/packages/flutter/lib` folder.
+ */
+ void configureFlutterPkg(Map<String, String> pathToCode) {
+ pathToCode.forEach((path, code) {
+ provider.newFile('$flutterPkgLibPath/$path', code);
+ });
+ // configure SourceFactory
+ Folder myPkgFolder = provider.getResource(flutterPkgLibPath);
+ UriResolver pkgResolver = new PackageMapUriResolver(provider, {
+ 'flutter': [myPkgFolder]
+ });
+ SourceFactory sourceFactory = new SourceFactory(
+ [new DartUriResolver(sdk), pkgResolver, resourceResolver]);
+ driver.configure(sourceFactory: sourceFactory);
+ // force 'flutter' resolution
+ addSource(
+ '/tmp/other.dart',
+ pathToCode.keys
+ .map((path) => "import 'package:flutter/$path';")
+ .join('\n'));
+ }
+
+ CompletionContributor createContributor();
+
+ void failedCompletion(String message,
+ [Iterable<CompletionSuggestion> completions]) {
+ StringBuffer sb = new StringBuffer(message);
+ if (completions != null) {
+ sb.write('\n found');
+ completions.toList()
+ ..sort(suggestionComparator)
+ ..forEach((CompletionSuggestion suggestion) {
+ sb.write('\n ${suggestion.completion} -> $suggestion');
+ });
+ }
+ fail(sb.toString());
+ }
+
+ CompletionSuggestion getSuggest(
+ {String completion: null,
+ CompletionSuggestionKind csKind: null,
+ ElementKind elemKind: null}) {
+ CompletionSuggestion cs;
+ if (suggestions != null) {
+ suggestions.forEach((CompletionSuggestion s) {
+ if (completion != null && completion != s.completion) {
+ return;
+ }
+ if (csKind != null && csKind != s.kind) {
+ return;
+ }
+ if (elemKind != null) {
+ Element element = s.element;
+ if (element == null || elemKind != element.kind) {
+ return;
+ }
+ }
+ if (cs == null) {
+ cs = s;
+ } else {
+ failedCompletion('expected exactly one $cs',
+ suggestions.where((s) => s.completion == completion));
+ }
+ });
+ }
+ return cs;
+ }
+
+ Future/*<E>*/ performAnalysis/*<E>*/(
+ int times, Completer/*<E>*/ completer) async {
+ if (completer.isCompleted) {
+ return completer.future;
+ }
+ // We use a delayed future to allow microtask events to finish. The
+ // Future.value or Future() constructors use scheduleMicrotask themselves and
+ // would therefore not wait for microtask callbacks that are scheduled after
+ // invoking this method.
+ return new Future.delayed(
+ Duration.ZERO, () => performAnalysis(times - 1, completer));
+ }
+
+ void resolveSource(String path, String content) {
+ addSource(path, content);
+ }
+
+ @override
+ void setUp() {
+ super.setUp();
+ contributor = createContributor();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698