| 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();
|
| + }
|
| +}
|
|
|