blob: c45323144af0b0ef740f12171714162edef32b7c [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/file_system/file_system.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);
}
@ReflectiveTestCase()
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');
}
}
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');
});
}
/**
* 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]);
{
AnalysisOptionsImpl options =
new AnalysisOptionsImpl.con1(context2.analysisOptions);
options.enableAsync = true;
options.enableEnum = true;
context2.analysisOptions = options;
}
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.compilationUnit;
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');
});
}
@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";');
});
}
}