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