blob: d67c2d8663b434c922e2435db8af9e319e174a6a [file] [log] [blame]
// 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.toplevel;
import 'package:analysis_server/src/protocol.dart';
import 'package:analysis_server/src/services/completion/completion_manager.dart';
import 'package:analysis_server/src/services/completion/dart_completion_cache.dart';
import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
import 'package:analysis_server/src/services/completion/imported_computer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:unittest/unittest.dart';
import '../../abstract_context.dart';
import '../../reflective_tests.dart';
import 'completion_test_util.dart';
main() {
groupSep = ' | ';
runReflectiveTests(ImportedComputerTest);
}
@reflectiveTest
class ImportedComputerTest extends AbstractSelectorSuggestionTest {
void assertCached(String completion) {
DartCompletionCache cache = request.cache;
if (!isCached(cache.importedTypeSuggestions, completion) &&
!isCached(cache.importedVoidReturnSuggestions, completion) &&
!isCached(cache.libraryPrefixSuggestions, completion) &&
!isCached(cache.otherImportedSuggestions, completion)) {
fail('expected $completion to be cached');
}
}
/**
* Assert that the ImportedComputer uses cached results to produce identical
* suggestions to the original set of suggestions.
*/
@override
void assertCachedCompute(_) {
if (!(computer as ImportedComputer).shouldWaitForLowPrioritySuggestions) {
return;
}
expect(request.unit.element, isNotNull);
List<CompletionSuggestion> oldSuggestions = request.suggestions;
/*
* Simulate a source change to flush the cached compilation unit
*/
ChangeSet changes = new ChangeSet();
changes.addedSource(testSource);
context.applyChanges(changes);
/*
* Calculate a new completion at the same location
*/
setUpComputer();
int replacementOffset = request.replacementOffset;
int replacementLength = request.replacementLength;
request = new DartCompletionRequest(
context,
searchEngine,
testSource,
completionOffset,
cache,
new CompletionPerformance());
request.replacementOffset = replacementOffset;
request.replacementLength = replacementLength;
expect(computeFast(), isTrue);
expect(request.unit.element, isNull);
List<CompletionSuggestion> newSuggestions = request.suggestions;
if (newSuggestions.length == oldSuggestions.length) {
if (!oldSuggestions.any(
(CompletionSuggestion s) => !newSuggestions.contains(s))) {
return;
}
}
StringBuffer sb = new StringBuffer(
'suggestions based upon cached results do not match expectations');
sb.write('\n Expected:');
oldSuggestions.toList()
..sort(suggestionComparator)
..forEach((CompletionSuggestion suggestion) {
sb.write('\n ${suggestion.completion} -> $suggestion');
});
sb.write('\n Actual:');
newSuggestions.toList()
..sort(suggestionComparator)
..forEach((CompletionSuggestion suggestion) {
sb.write('\n ${suggestion.completion} -> $suggestion');
});
fail(sb.toString());
}
void assertNotCached(String completion) {
DartCompletionCache cache = request.cache;
if (isCached(cache.importedTypeSuggestions, completion) ||
isCached(cache.importedVoidReturnSuggestions, completion) ||
isCached(cache.libraryPrefixSuggestions, completion) ||
isCached(cache.otherImportedSuggestions, completion)) {
fail('expected $completion NOT to be cached');
}
}
@override
CompletionSuggestion assertSuggestImportedField(String name, String type,
{int relevance: DART_RELEVANCE_INHERITED_FIELD}) {
return assertSuggestField(name, type, relevance: relevance);
}
CompletionSuggestion assertSuggestImportedGetter(String name,
String returnType, {int relevance: DART_RELEVANCE_INHERITED_ACCESSOR}) {
return assertSuggestGetter(name, returnType, relevance: relevance);
}
CompletionSuggestion assertSuggestImportedMethod(String name,
String declaringType, String returnType, {int relevance:
DART_RELEVANCE_INHERITED_METHOD}) {
return assertSuggestMethod(
name,
declaringType,
returnType,
relevance: relevance);
}
CompletionSuggestion assertSuggestImportedSetter(String name, {int relevance:
DART_RELEVANCE_INHERITED_ACCESSOR}) {
return assertSuggestSetter(name, relevance);
}
bool isCached(List<CompletionSuggestion> suggestions, String completion) =>
suggestions.any((CompletionSuggestion s) => s.completion == completion);
@override
void setUpComputer() {
computer = new ImportedComputer(shouldWaitForLowPrioritySuggestions: true);
}
@override
test_ArgumentList() {
return super.test_ArgumentList().then((_) {
expect(request.cache.importKey, "import '/libA.dart';");
ClassElement objClassElem1 = request.cache.importedClassMap['Object'];
expect(objClassElem1, isNotNull);
ClassElement objClassElem2 = request.cache.objectClassElement;
expect(objClassElem1, same(objClassElem2));
});
}
@override
test_ArgumentList_imported_function() {
return super.test_ArgumentList_imported_function().then((_) {
expect(request.cache.importKey, "import '/libA.dart';");
});
}
@override
test_AssignmentExpression_RHS() {
return super.test_AssignmentExpression_RHS().then((_) {
expect(request.cache.importKey, '');
});
}
@override
test_Block() {
return super.test_Block().then((_) {
expect(
request.cache.importKey,
'import "/testAB.dart";import "/testCD.dart" hide D;import "/testEEF.dart" show EE;import "/testG.dart" as g;');
assertCached('A');
assertCached('T3');
});
}
@override
test_Block_inherited_imported() {
return super.test_Block_inherited_imported().then((_) {
assertCached('E');
assertCached('F');
assertNotCached('e1');
assertNotCached('i2');
assertNotCached('m1');
});
}
test_Block_partial_results() {
// Block BlockFunctionBody MethodDeclaration
addSource('/testAB.dart', '''
export "dart:math" hide max;
class A {int x;}
@deprecated D1() {int x;}
class _B { }''');
addSource('/testCD.dart', '''
String T1;
var _T2;
class C { }
class D { }''');
addSource('/testEEF.dart', '''
class EE { }
class F { }''');
addSource('/testG.dart', 'class G { }');
addSource('/testH.dart', '''
class H { }
int T3;
var _T4;'''); // not imported
addTestSource('''
import "/testAB.dart";
import "/testCD.dart" hide D;
import "/testEEF.dart" show EE;
import "/testG.dart" as g;
int T5;
var _T6;
Z D2() {int x;}
class X {a() {var f; {var x;} ^ var r;} void b() { }}
class Z { }''');
(computer as ImportedComputer).shouldWaitForLowPrioritySuggestions = false;
computeFast();
return computeFull((bool result) {
assertSuggestImportedClass('C');
// Assert computer does not wait for or include low priority results
// from non-imported libraries unless instructed to do so.
assertNotSuggested('H');
});
}
test_function_parameters_mixed_required_and_named() {
addSource('/libA.dart', '''
void m(x, {int y}) {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 1);
expect(suggestion.hasNamedParameters, true);
});
}
test_function_parameters_mixed_required_and_positional() {
addSource('/libA.dart', '''
void m(x, [int y]) {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 1);
expect(suggestion.hasNamedParameters, false);
});
}
test_function_parameters_named() {
addSource('/libA.dart', '''
void m({x, int y}) {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, true);
});
}
test_function_parameters_none() {
addSource('/libA.dart', '''
void m() {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
computeFast();
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, isEmpty);
expect(suggestion.parameterTypes, isEmpty);
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, false);
});
}
test_function_parameters_positional() {
addSource('/libA.dart', '''
void m([x, int y]) {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, false);
});
}
test_function_parameters_required() {
addSource('/libA.dart', '''
void m(x, int y) {}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestFunction('m', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 2);
expect(suggestion.hasNamedParameters, false);
});
}
test_method_parameters_mixed_required_and_named() {
addSource('/libA.dart', '''
class A {
void m(x, {int y}) {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 1);
expect(suggestion.hasNamedParameters, true);
});
}
test_method_parameters_mixed_required_and_positional() {
addSource('/libA.dart', '''
class A {
void m(x, [int y]) {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 1);
expect(suggestion.hasNamedParameters, false);
});
}
test_method_parameters_named() {
addSource('/libA.dart', '''
class A {
void m({x, int y}) {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, true);
});
}
test_method_parameters_none() {
addSource('/libA.dart', '''
class A {
void m() {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
computeFast();
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, isEmpty);
expect(suggestion.parameterTypes, isEmpty);
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, false);
});
}
test_method_parameters_positional() {
addSource('/libA.dart', '''
class A {
void m([x, int y]) {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 0);
expect(suggestion.hasNamedParameters, false);
});
}
test_method_parameters_required() {
addSource('/libA.dart', '''
class A {
void m(x, int y) {}
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion =
assertSuggestImportedMethod('m', 'A', 'void');
expect(suggestion.parameterNames, hasLength(2));
expect(suggestion.parameterNames[0], 'x');
expect(suggestion.parameterTypes[0], 'dynamic');
expect(suggestion.parameterNames[1], 'y');
expect(suggestion.parameterTypes[1], 'int');
expect(suggestion.requiredParameterCount, 2);
expect(suggestion.hasNamedParameters, false);
});
}
test_mixin_ordering() {
// TODO(paulberry): The mixins are visited in the correct order, so we see
// M2.m() before M1.m(), as we should. But the second (shadowed) result
// isn't being thrown out as it should.
addSource('/libA.dart', '''
class B {}
class M1 {
void m() {}
}
class M2 {
void m() {}
}
''');
addTestSource('''
import '/libA.dart';
class C extends B with M1, M2 {
void f() {
^
}
}
''');
return computeFull((bool result) {
assertSuggestImportedMethod('m', 'M2', 'void');
});
}
/**
* Ensure that completions in one context don't appear in another
*/
test_multiple_contexts() {
// Create a 2nd context with source
var context2 = AnalysisEngine.instance.createAnalysisContext();
context2.sourceFactory =
new SourceFactory([AbstractContextTest.SDK_RESOLVER, resourceResolver]);
String content2 = 'class ClassFromAnotherContext { }';
Source source2 =
provider.newFile('/context2/foo.dart', content2).createSource();
ChangeSet changeSet = new ChangeSet();
changeSet.addedSource(source2);
context2.applyChanges(changeSet);
context2.setContents(source2, content2);
// Resolve the source in the 2nd context and update the index
var result = context2.performAnalysisTask();
while (result.hasMoreWork) {
result.changeNotices.forEach((ChangeNotice notice) {
CompilationUnit unit = notice.resolvedDartUnit;
if (unit != null) {
index.indexUnit(context2, unit);
}
});
result = context2.performAnalysisTask();
}
// Check that source in 2nd context does not appear in completion in 1st
addSource('/context1/libA.dart', '''
library libA;
class ClassInLocalContext {int x;}''');
testFile = '/context1/completionTest.dart';
addTestSource('''
import "/context1/libA.dart";
import "/foo.dart";
main() {C^}
''');
computeFast();
return computeFull((bool result) {
assertSuggestImportedClass('ClassInLocalContext');
// Assert computer does not include results from 2nd context.
assertNotSuggested('ClassFromAnotherContext');
});
}
test_no_parameters_field() {
addSource('/libA.dart', '''
class A {
int x;
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestImportedField('x', 'int');
assertHasNoParameterInfo(suggestion);
});
}
test_no_parameters_getter() {
addSource('/libA.dart', '''
class A {
int get x => null;
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestImportedGetter('x', 'int');
assertHasNoParameterInfo(suggestion);
});
}
test_no_parameters_setter() {
addSource('/libA.dart', '''
class A {
set x(int value) {};
}
''');
addTestSource('''
import '/libA.dart';
class B extends A {
main() {^}
}
''');
return computeFull((bool result) {
CompletionSuggestion suggestion = assertSuggestImportedSetter('x');
assertHasNoParameterInfo(suggestion);
});
}
@override
test_partFile_TypeName() {
return super.test_partFile_TypeName().then((_) {
expect(request.cache.importKey, 'part of libA;');
});
}
@override
test_partFile_TypeName2() {
return super.test_partFile_TypeName2().then((_) {
expect(
request.cache.importKey,
'library libA;import "/testB.dart";part "/testA.dart";');
});
}
}