Index: pkg/analysis_server/test/services/completion/dart/dart_completion_contributor_test.dart |
diff --git a/pkg/analysis_server/test/services/completion/dart/dart_completion_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/dart_completion_contributor_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cb4c2181f604a9b98b9d016fbfcb91a2ef7e6c98 |
--- /dev/null |
+++ b/pkg/analysis_server/test/services/completion/dart/dart_completion_contributor_test.dart |
@@ -0,0 +1,225 @@ |
+// 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. |
+ |
+library test.services.completion.contributor.dart; |
+ |
+import 'dart:async'; |
+ |
+import 'package:analysis_server/plugin/protocol/protocol.dart' as protocol |
+ show Element, ElementKind; |
+import 'package:analysis_server/plugin/protocol/protocol.dart' |
+ hide Element, ElementKind; |
+import 'package:analysis_server/plugin/protocol/protocol.dart'; |
+import 'package:analysis_server/src/provisional/completion/completion_dart.dart' |
+ show DartCompletionContributor, DartCompletionRequest; |
+import 'package:analysis_server/src/provisional/completion/dart/completion_target.dart'; |
+import 'package:analysis_server/src/services/completion/completion_core.dart' |
+ show CompletionRequestImpl; |
+import 'package:analysis_server/src/services/completion/completion_dart.dart' |
+ show DartCompletionRequestImpl; |
+import 'package:analysis_server/src/services/completion/dart/dart_completion_manager.dart'; |
+import 'package:analysis_server/src/services/completion/dart/reference_contributor.dart'; |
+import 'package:analysis_server/src/services/completion/dart_completion_manager.dart' |
+ show DART_RELEVANCE_DEFAULT, DART_RELEVANCE_LOW; |
+import 'package:analysis_server/src/services/index/index.dart'; |
+import 'package:analysis_server/src/services/index/local_memory_index.dart'; |
+import 'package:analysis_server/src/services/search/search_engine_internal.dart'; |
+import 'package:analyzer/src/cancelable_future.dart'; |
+import 'package:analyzer/src/context/context.dart'; |
+import 'package:analyzer/src/generated/ast.dart'; |
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl; |
+import 'package:analyzer/src/generated/source.dart'; |
+import 'package:analyzer/src/task/dart.dart'; |
+import 'package:analyzer/task/dart.dart'; |
+import 'package:analyzer/task/model.dart'; |
+import 'package:unittest/unittest.dart'; |
+ |
+import '../../../abstract_context.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 { |
+ Index index; |
+ SearchEngineImpl searchEngine; |
+ String testFile = '/completionTest.dart'; |
+ Source testSource; |
+ int completionOffset; |
+ DartCompletionContributor contributor; |
+ DartCompletionRequest request; |
+ List<CompletionSuggestion> suggestions; |
+ |
+ 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 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); |
+ } |
+ |
+ CompletionSuggestion 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'); |
+ } |
+ return null; |
+ } |
+ |
+ CompletionSuggestion assertSuggest(String completion, |
+ {CompletionSuggestionKind csKind: CompletionSuggestionKind.INVOCATION, |
+ int relevance: DART_RELEVANCE_DEFAULT, |
+ String importUri, |
+ protocol.ElementKind elemKind: null, |
+ bool isDeprecated: false, |
+ bool isPotential: false, |
+ String elemFile, |
+ int elemOffset}) { |
+ 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)); |
+ } |
+ expect(cs.importUri, importUri); |
+ expect(cs.selectionOffset, equals(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); |
+ } |
+ return cs; |
+ } |
+ |
+ Future computeSuggestions([int times = 200]) async { |
+ // Build a request with a target based on a parsed unit |
+ CompilationUnit unit = context.parseCompilationUnit(testSource); |
+// AnalysisServer server = new AnalysisServerMock( |
+// searchEngine: searchEngine, resourceProvider: provider); |
+ request = new DartCompletionRequestImpl( |
+ new CompletionRequestImpl( |
+ context, provider, testSource, completionOffset), |
+ new CompletionTarget.forOffset(unit, completionOffset)); |
+ |
+ // Compute suggestions |
+ DartSuggestionCollectorImpl collector = new DartSuggestionCollectorImpl(); |
+ var future = contributor.computeSuggestions(request, collector); |
+ if (future != null) { |
+ Completer<CompilationUnit> completer = new Completer<CompilationUnit>(); |
+ future.then((_) => completer.complete()); |
+ await performAnalysis(times, completer); |
+ expect(completer.isCompleted, isTrue, |
+ reason: 'Expected suggestions in $times iterations'); |
+ } |
+ suggestions = collector.suggestions; |
+ } |
+ |
+ DartCompletionContributor 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'); |
+ }); |
+ } |
+ if (request.parsedTarget.containingNode != null) { |
+ sb.write('\n in'); |
+ AstNode node = request.parsedTarget.containingNode; |
+ while (node != null) { |
+ sb.write('\n ${node.runtimeType}'); |
+ node = node.parent; |
+ } |
+ } |
+ fail(sb.toString()); |
+ } |
+ |
+ CompletionSuggestion getSuggest( |
+ {String completion: null, |
+ CompletionSuggestionKind csKind: null, |
+ protocol.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) { |
+ protocol.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 performAnalysis(int times, Completer completer) { |
+ if (completer.isCompleted) return completer.future; |
+ if (times == 0) return new Future.value(null); |
+ context.performAnalysisTask(); |
+ // 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)); |
+ } |
+ |
+ @override |
+ void setUp() { |
+ AnalysisEngine.instance.useTaskModel = true; |
+ super.setUp(); |
+ index = createLocalMemoryIndex(); |
+ searchEngine = new SearchEngineImpl(index); |
+ contributor = createContributor(); |
+ } |
+} |