blob: 37bf03316ea3a48046b2876f325a4da5a1588216 [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.
import 'dart:async';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/domain_analysis.dart';
import 'package:analysis_server/src/domain_completion.dart';
import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
import 'package:analysis_server/src/utilities/mocks.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_utilities/check/check.dart';
import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'domain_completion_util.dart';
import 'mocks.dart';
import 'services/completion/dart/completion_check.dart';
import 'src/plugin/plugin_manager_test.dart';
import 'utils/change_check.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(CompletionDomainHandlerGetSuggestionDetails2Test);
defineReflectiveTests(CompletionDomainHandlerGetSuggestions2Test);
defineReflectiveTests(CompletionDomainHandlerGetSuggestionsTest);
});
}
/// TODO(scheglov) this is duplicate
class AnalysisOptionsFileConfig {
final List<String> experiments;
final bool implicitCasts;
final bool implicitDynamic;
final List<String> lints;
final bool strictCasts;
final bool strictInference;
final bool strictRawTypes;
AnalysisOptionsFileConfig({
this.experiments = const [],
this.implicitCasts = true,
this.implicitDynamic = true,
this.lints = const [],
this.strictCasts = false,
this.strictInference = false,
this.strictRawTypes = false,
});
String toContent() {
var buffer = StringBuffer();
buffer.writeln('analyzer:');
buffer.writeln(' enable-experiment:');
for (var experiment in experiments) {
buffer.writeln(' - $experiment');
}
buffer.writeln(' language:');
buffer.writeln(' strict-casts: $strictCasts');
buffer.writeln(' strict-inference: $strictInference');
buffer.writeln(' strict-raw-types: $strictRawTypes');
buffer.writeln(' strong-mode:');
buffer.writeln(' implicit-casts: $implicitCasts');
buffer.writeln(' implicit-dynamic: $implicitDynamic');
buffer.writeln('linter:');
buffer.writeln(' rules:');
for (var lint in lints) {
buffer.writeln(' - $lint');
}
return buffer.toString();
}
}
@reflectiveTest
class CompletionDomainHandlerGetSuggestionDetails2Test
extends PubPackageAnalysisServerTest {
Future<void> test_alreadyImported() async {
await _configureWithWorkspaceRoot();
var details = await _getTestCodeDetails('''
import 'dart:math';
void f() {
Rand^
}
''', completion: 'Random', libraryUri: 'dart:math');
check(details)
..completion.isEqualTo('Random')
..change.edits.isEmpty;
}
Future<void> test_import_dart() async {
await _configureWithWorkspaceRoot();
var details = await _getTestCodeDetails('''
void f() {
R^
}
''', completion: 'Random', libraryUri: 'dart:math');
check(details)
..completion.isEqualTo('Random')
..change
.hasFileEdit(testFilePathPlatform)
.appliedTo(testFileContent)
.isEqualTo(r'''
import 'dart:math';
void f() {
R
}
''');
}
Future<void> test_import_package_dependencies() async {
writeTestPackagePubspecYamlFile(r'''
name: test
dependencies:
aaa: any
''');
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
newFile('${aaaRoot.path}/lib/f.dart', content: '''
class Test {}
''');
writeTestPackageConfig(
config: PackageConfigFileBuilder()
..add(name: 'aaa', rootPath: aaaRoot.path),
);
await _configureWithWorkspaceRoot();
var details = await _getTestCodeDetails('''
void f() {
T^
}
''', completion: 'Test', libraryUri: 'package:aaa/a.dart');
check(details)
..completion.isEqualTo('Test')
..change
.hasFileEdit(testFilePathPlatform)
.appliedTo(testFileContent)
.isEqualTo(r'''
import 'package:aaa/a.dart';
void f() {
T
}
''');
}
Future<void> test_import_package_this() async {
newFile('$testPackageLibPath/a.dart', content: '''
class Test {}
''');
await _configureWithWorkspaceRoot();
var details = await _getTestCodeDetails('''
void f() {
T^
}
''', completion: 'Test', libraryUri: 'package:test/a.dart');
check(details)
..completion.isEqualTo('Test')
..change
.hasFileEdit(testFilePathPlatform)
.appliedTo(testFileContent)
.isEqualTo(r'''
import 'package:test/a.dart';
void f() {
T
}
''');
}
Future<void> test_invalidLibraryUri() async {
await _configureWithWorkspaceRoot();
var request = CompletionGetSuggestionDetails2Params(
testFilePathPlatform, 0, 'Random', '[foo]:bar')
.toRequest('0');
var response = await handleRequest(request);
expect(response.error?.code, RequestErrorCode.INVALID_PARAMETER);
// TODO(scheglov) Check that says "libraryUri".
}
Future<void> test_invalidPath() async {
await _configureWithWorkspaceRoot();
var request =
CompletionGetSuggestionDetails2Params('foo', 0, 'Random', 'dart:math')
.toRequest('0');
var response = await handleRequest(request);
expect(response.error?.code, RequestErrorCode.INVALID_FILE_PATH_FORMAT);
}
Future<CompletionGetSuggestionDetails2Result> _getCodeDetails({
required String path,
required String content,
required String completion,
required String libraryUri,
}) async {
var completionOffset = content.indexOf('^');
expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
var nextOffset = content.indexOf('^', completionOffset + 1);
expect(nextOffset, equals(-1), reason: 'too many ^');
newFile(path,
content: content.substring(0, completionOffset) +
content.substring(completionOffset + 1));
return await _getDetails(
path: path,
completionOffset: completionOffset,
completion: completion,
libraryUri: libraryUri,
);
}
Future<CompletionGetSuggestionDetails2Result> _getDetails({
required String path,
required int completionOffset,
required String completion,
required String libraryUri,
}) async {
var request = CompletionGetSuggestionDetails2Params(
path,
completionOffset,
completion,
libraryUri,
).toRequest('0');
var response = await _handleSuccessfulRequest(request);
return CompletionGetSuggestionDetails2Result.fromResponse(response);
}
Future<CompletionGetSuggestionDetails2Result> _getTestCodeDetails(
String content, {
required String completion,
required String libraryUri,
}) async {
return _getCodeDetails(
path: convertPath(testFilePath),
content: content,
completion: completion,
libraryUri: libraryUri,
);
}
}
@reflectiveTest
class CompletionDomainHandlerGetSuggestions2Test
extends PubPackageAnalysisServerTest {
@override
void setUp() {
super.setUp();
completionDomain.budgetDuration = const Duration(seconds: 30);
}
Future<void> test_abort_onAnotherCompletionRequest() async {
var abortedIdSet = <String>{};
server.discardedRequests.stream.listen((request) {
abortedIdSet.add(request.id);
});
newFile(testFilePath, content: '');
await _configureWithWorkspaceRoot();
// Send three requests, the first two should be aborted.
var request0 = _sendTestCompletionRequest('0', 0);
var request1 = _sendTestCompletionRequest('1', 0);
var request2 = _sendTestCompletionRequest('2', 0);
// Wait for all three.
var response0 = await request0.toResponse();
var response1 = await request1.toResponse();
var response2 = await request2.toResponse();
// The first two should be aborted.
expect(abortedIdSet, {'0', '1'});
check(response0)
..assertIncomplete()
..suggestions.isEmpty;
check(response1)
..assertIncomplete()
..suggestions.isEmpty;
check(response2)
..assertComplete()
..suggestions.containsMatch(
(suggestion) => suggestion.completion.isEqualTo('int'),
);
}
Future<void> test_abort_onUpdateContent() async {
var abortedIdSet = <String>{};
server.discardedRequests.stream.listen((request) {
abortedIdSet.add(request.id);
});
newFile(testFilePath, content: '');
await _configureWithWorkspaceRoot();
// Schedule a completion request.
var request = _sendTestCompletionRequest('0', 0);
// Simulate typing in the IDE.
await _handleSuccessfulRequest(
AnalysisUpdateContentParams({
testFilePathPlatform: AddContentOverlay('void f() {}'),
}).toRequest('1'),
);
// The request should be aborted.
var response = await request.toResponse();
expect(abortedIdSet, {'0'});
check(response)
..assertIncomplete()
..suggestions.isEmpty;
}
Future<void> test_isNotImportedFeature_prefixed_classInstanceMethod() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A {
void foo01() {}
}
''');
newFile('$testPackageLibPath/b.dart', content: '''
import 'a.dart';
class B extends A {
void foo02() {}
}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f(B b) {
b.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// The fact that `b.dart` is imported, and `a.dart` is not, does not affect
// the order of suggestions added with an expression prefix. We are not
// going to import anything, so this does not matter.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
]);
}
Future<void> test_notImported_dart() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
void f() {
Rand^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('Random')
..libraryUriToImport.isEqualTo('dart:math'),
]);
}
Future<void> test_notImported_emptyBudget() async {
await _configureWithWorkspaceRoot();
// Empty budget, so no not yet imported libraries.
completionDomain.budgetDuration = const Duration(milliseconds: 0);
var response = await _getTestCodeSuggestions('''
void f() {
Rand^
}
''');
check(response)
..assertIncomplete()
..hasReplacement(left: 4)
..suggestions.withElementClass.isEmpty;
}
Future<void> test_notImported_lowerRelevance_enumConstant() async {
newFile('$testPackageLibPath/a.dart', content: '''
enum E1 {
foo01
}
''');
newFile('$testPackageLibPath/b.dart', content: '''
enum E2 {
foo02
}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('E2.foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('E1.foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_extension_getter() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
int get foo01 => 0;
}
''');
newFile('$testPackageLibPath/b.dart', content: '''
extension E2 on int {
int get foo02 => 0;
}
''');
var response = await _getTestCodeSuggestions(r'''
import 'b.dart';
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_extension_method() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
void foo01() {}
}
''');
newFile('$testPackageLibPath/b.dart', content: '''
extension E2 on int {
void foo02() {}
}
''');
var response = await _getTestCodeSuggestions(r'''
import 'b.dart';
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_extension_setter() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
set foo01(int _) {}
}
''');
newFile('$testPackageLibPath/b.dart', content: '''
extension E2 on int {
set foo02(int _) {}
}
''');
var response = await _getTestCodeSuggestions(r'''
import 'b.dart';
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_topLevel_class() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
class A02 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
// `A01` relevance is decreased because it is not yet imported.
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_topLevel_getter() async {
newFile('$testPackageLibPath/a.dart', content: '''
int get foo01 => 0;
''');
newFile('$testPackageLibPath/b.dart', content: '''
int get foo02 => 0;
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_topLevel_setter() async {
newFile('$testPackageLibPath/a.dart', content: '''
set foo01(int _) {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
set foo02(int _) {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_lowerRelevance_topLevel_variable() async {
newFile('$testPackageLibPath/a.dart', content: '''
var foo01 = 0;
''');
newFile('$testPackageLibPath/b.dart', content: '''
var foo02 = 0;
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'b.dart';
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo01` relevance is decreased because it is not yet imported.
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_pub_dependencies_inLib() async {
writeTestPackagePubspecYamlFile(r'''
name: test
dependencies:
aaa: any
dev_dependencies:
bbb: any
''');
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
newFile('${aaaRoot.path}/lib/f.dart', content: '''
class A01 {}
''');
newFile('${aaaRoot.path}/lib/src/f.dart', content: '''
class A02 {}
''');
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
newFile('${bbbRoot.path}/lib/f.dart', content: '''
class A03 {}
''');
newFile('${bbbRoot.path}/lib/src/f.dart', content: '''
class A04 {}
''');
writeTestPackageConfig(
config: PackageConfigFileBuilder()
..add(name: 'aaa', rootPath: aaaRoot.path)
..add(name: 'bbb', rootPath: bbbRoot.path),
);
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:aaa/f.dart'),
]);
}
Future<void> test_notImported_pub_dependencies_inTest() async {
writeTestPackagePubspecYamlFile(r'''
name: test
dependencies:
aaa: any
dev_dependencies:
bbb: any
''');
var aaaRoot = getFolder('$workspaceRootPath/packages/aaa');
newFile('${aaaRoot.path}/lib/f.dart', content: '''
class A01 {}
''');
newFile('${aaaRoot.path}/lib/src/f.dart', content: '''
class A02 {}
''');
var bbbRoot = getFolder('$workspaceRootPath/packages/bbb');
newFile('${bbbRoot.path}/lib/f.dart', content: '''
class A03 {}
''');
newFile('${bbbRoot.path}/lib/src/f.dart', content: '''
class A04 {}
''');
writeTestPackageConfig(
config: PackageConfigFileBuilder()
..add(name: 'aaa', rootPath: aaaRoot.path)
..add(name: 'bbb', rootPath: bbbRoot.path),
);
await _configureWithWorkspaceRoot();
var test_path = convertPath('$testPackageTestPath/test.dart');
var response = await _getCodeSuggestions(
path: test_path,
content: '''
void f() {
A0^
}
''',
);
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:aaa/f.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A03')
..libraryUriToImport.isEqualTo('package:bbb/f.dart'),
]);
}
Future<void> test_notImported_pub_this() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
class A02 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isEqualTo('package:test/b.dart'),
]);
}
Future<void> test_notImported_pub_this_hasImport() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
class A02 {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
class A03 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'a.dart';
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('A03')
..libraryUriToImport.isEqualTo('package:test/b.dart'),
]);
}
Future<void> test_notImported_pub_this_hasImport_hasShow() async {
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
class A02 {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
class A03 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'a.dart' show A02;
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
// Note:
// 1. A02 is the first, because it is already imported.
// 2. A01 is still suggested, but with lower relevance.
// 3. A03 has the same relevance (not tested), but sorted by name.
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isNull,
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A03')
..libraryUriToImport.isEqualTo('package:test/b.dart'),
]);
}
Future<void> test_notImported_pub_this_inLib_excludesTest() async {
writeTestPackagePubspecYamlFile(r'''
name: test
''');
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
''');
newFile('$testPackageTestPath/b.dart', content: '''
class A02 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_notImported_pub_this_inLib_includesThisSrc() async {
writeTestPackagePubspecYamlFile(r'''
name: test
''');
newFile('$testPackageLibPath/f.dart', content: '''
class A01 {}
''');
newFile('$testPackageLibPath/src/f.dart', content: '''
class A02 {}
''');
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/f.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isEqualTo('package:test/src/f.dart'),
]);
}
Future<void> test_notImported_pub_this_inTest_includesTest() async {
writeTestPackagePubspecYamlFile(r'''
name: test
''');
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
''');
var b = newFile('$testPackageTestPath/b.dart', content: '''
class A02 {}
''');
var b_uriStr = toUriStr(b.path);
await _configureWithWorkspaceRoot();
var test_path = convertPath('$testPackageTestPath/test.dart');
var response = await _getCodeSuggestions(
path: test_path,
content: '''
void f() {
A0^
}
''',
);
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isEqualTo(b_uriStr),
]);
}
Future<void> test_notImported_pub_this_inTest_includesThisSrc() async {
writeTestPackagePubspecYamlFile(r'''
name: test
''');
newFile('$testPackageLibPath/f.dart', content: '''
class A01 {}
''');
newFile('$testPackageLibPath/src/f.dart', content: '''
class A02 {}
''');
await _configureWithWorkspaceRoot();
var test_path = convertPath('$testPackageTestPath/test.dart');
var response = await _getCodeSuggestions(
path: test_path,
content: '''
void f() {
A0^
}
''',
);
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..libraryUriToImport.isEqualTo('package:test/f.dart'),
(suggestion) => suggestion
..completion.isEqualTo('A02')
..libraryUriToImport.isEqualTo('package:test/src/f.dart'),
]);
}
Future<void> test_numResults_class_methods() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
class A {
void foo01() {}
void foo02() {}
void foo03() {}
}
void f(A a) {
a.foo0^
}
''', maxResults: 2);
check(response)
..assertIncomplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isMethodInvocation,
]);
}
Future<void> test_numResults_topLevelVariables() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
var foo01 = 0;
var foo02 = 0;
var foo03 = 0;
void f() {
foo0^
}
''', maxResults: 2);
check(response)
..assertIncomplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isTopLevelVariable,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isTopLevelVariable,
]);
}
Future<void> test_numResults_topLevelVariables_imported_withPrefix() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
var foo01 = 0;
var foo02 = 0;
var foo03 = 0;
''');
var response = await _getTestCodeSuggestions('''
import 'a.dart' as prefix;
void f() {
prefix.^
}
''', maxResults: 2);
check(response)
..assertIncomplete()
..hasEmptyReplacement();
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isTopLevelVariable,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isTopLevelVariable,
]);
}
Future<void> test_prefixed_class_constructors() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
class A {
A.foo01();
A.foo02();
}
void f() {
A.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isConstructorInvocation,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isConstructorInvocation,
]);
}
Future<void> test_prefixed_class_getters() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
class A {
int get foo01 => 0;
int get foo02 => 0;
}
void f(A a) {
a.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter
..libraryUri.isNull
..isNotImported.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isGetter
..libraryUri.isNull
..isNotImported.isNull,
]);
}
Future<void> test_prefixed_class_methods_instance() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
class A {
void foo01() {}
void foo02() {}
}
void f(A a) {
a.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isMethodInvocation,
]);
}
Future<void> test_prefixed_class_methods_static() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
class A {
static void foo01() {}
static void foo02() {}
}
void f() {
A.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isMethodInvocation,
]);
}
Future<void> test_prefixed_expression_extensionGetters() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
extension E1 on int {
int get foo01 => 0;
int get foo02 => 0;
int get bar => 0;
}
extension E2 on double {
int get foo03 => 0;
}
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isGetter,
]);
}
Future<void> test_prefixed_expression_extensionGetters_notImported() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
int get foo01 => 0;
int get bar => 0;
}
extension E2 on int {
int get foo02 => 0;
}
extension E3 on double {
int get foo03 => 0;
}
''');
var response = await _getTestCodeSuggestions(r'''
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isGetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void>
test_prefixed_expression_extensionGetters_notImported_private() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
int get foo01 => 0;
}
extension _E2 on int {
int get foo02 => 0;
}
extension on int {
int get foo03 => 0;
}
''');
var response = await _getTestCodeSuggestions(r'''
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_prefixed_expression_extensionMethods() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
extension E1 on int {
void foo01() {}
void foo02() {}
void bar() {}
}
extension E2 on double {
void foo03() {}
}
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isMethodInvocation,
]);
}
Future<void> test_prefixed_expression_extensionMethods_notImported() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
void foo01() {}
void bar() {}
}
extension E2 on int {
void foo02() {}
}
extension E3 on double {
void foo03() {}
}
''');
var response = await _getTestCodeSuggestions(r'''
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isMethodInvocation
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_prefixed_expression_extensionSetters() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
extension E1 on int {
set foo01(int _) {}
set foo02(int _) {}
set bar(int _) {}
}
extension E2 on double {
set foo03(int _) {}
}
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isSetter,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isSetter,
]);
}
Future<void> test_prefixed_expression_extensionSetters_notImported() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
set foo01(int _) {}
set bar(int _) {}
}
extension E2 on int {
set foo02(int _) {}
}
extension E3 on double {
set foo03(int _) {}
}
''');
var response = await _getTestCodeSuggestions(r'''
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isSetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isSetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void>
test_prefixed_expression_extensionSetters_notImported_private() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
set foo01(int _) {}
}
extension _E2 on int {
set foo02(int _) {}
}
extension on int {
set foo03(int _) {}
}
''');
var response = await _getTestCodeSuggestions(r'''
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isSetter
..libraryUriToImport.isEqualTo('package:test/a.dart'),
]);
}
Future<void> test_prefixed_extensionGetters_imported() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
extension E1 on int {
int get foo01 => 0;
int get foo02 => 0;
int get bar => 0;
}
extension E2 on double {
int get foo03 => 0;
}
''');
var response = await _getTestCodeSuggestions(r'''
import 'a.dart';
void f() {
0.foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isGetter,
]);
}
Future<void> test_prefixed_extensionOverride_extensionGetters() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
extension E1 on int {
int get foo01 => 0;
}
extension E2 on int {
int get foo02 => 0;
}
void f() {
E1(0).foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isGetter,
]);
}
Future<void> test_prefixed_extensionOverride_extensionMethods() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
extension E1 on int {
void foo01() {}
}
extension E2 on int {
void foo01() {}
}
void f() {
E1(0).foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isMethodInvocation,
]);
}
Future<void> test_prefixed_importPrefix_class() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'dart:math' as math;
void f() {
math.Rand^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('Random')
..libraryUri.isEqualTo('dart:math')
..isNotImported.isNull,
]);
}
Future<void> test_unprefixed_filters() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
var foo01 = 0;
var foo02 = 0;
var bar01 = 0;
var bar02 = 0;
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isTopLevelVariable,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isTopLevelVariable,
]);
}
Future<void> test_unprefixed_imported_class() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
class A01 {}
''');
newFile('$testPackageLibPath/b.dart', content: '''
class A02 {}
''');
var response = await _getTestCodeSuggestions('''
import 'a.dart';
import 'b.dart';
void f() {
A0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 2);
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('A01')
..isClass
..libraryUri.isEqualTo('package:test/a.dart')
..isNotImported.isNull,
(suggestion) => suggestion
..completion.isEqualTo('A02')
..isClass
..libraryUri.isEqualTo('package:test/b.dart')
..isNotImported.isNull,
]);
}
Future<void> test_unprefixed_imported_topLevelVariable() async {
await _configureWithWorkspaceRoot();
newFile('$testPackageLibPath/a.dart', content: '''
var foo01 = 0;
''');
newFile('$testPackageLibPath/b.dart', content: '''
var foo02 = 0;
''');
var response = await _getTestCodeSuggestions('''
import 'a.dart';
import 'b.dart';
void f() {
foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isTopLevelVariable
..libraryUri.isEqualTo('package:test/a.dart')
..isNotImported.isNull,
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isTopLevelVariable
..libraryUri.isEqualTo('package:test/b.dart')
..isNotImported.isNull,
]);
}
Future<void> test_unprefixed_imported_withPrefix_class() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions('''
import 'dart:math' as math;
void f() {
Rand^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// No suggestion without the `math` prefix.
check(response).suggestions.withElementClass.matches([
(suggestion) => suggestion
..completion.isEqualTo('math.Random')
..libraryUri.isEqualTo('dart:math')
..isNotImported.isNull,
]);
}
Future<void> test_unprefixed_sorts_byScore() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
var fooAB = 0;
var fooBB = 0;
void f() {
fooB^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `fooBB` has better score than `fooAB` - prefix match
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('fooBB')
..isTopLevelVariable,
(suggestion) => suggestion
..completion.isEqualTo('fooAB')
..isTopLevelVariable,
]);
}
Future<void> test_unprefixed_sorts_byType() async {
await _configureWithWorkspaceRoot();
var response = await _getTestCodeSuggestions(r'''
var foo01 = 0.0;
var foo02 = 0;
void f() {
int v = foo0^
}
''');
check(response)
..assertComplete()
..hasReplacement(left: 4);
// `foo02` has better relevance, its type matches the context type
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('foo02')
..isTopLevelVariable,
(suggestion) => suggestion
..completion.isEqualTo('foo01')
..isTopLevelVariable,
]);
}
Future<void> test_yaml_analysisOptions_root() async {
await _configureWithWorkspaceRoot();
var path = convertPath('$testPackageRootPath/analysis_options.yaml');
var response = await _getCodeSuggestions(
path: path,
content: '^',
);
check(response)
..assertComplete()
..hasEmptyReplacement();
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('analyzer: ')
..kind.isIdentifier,
(suggestion) => suggestion
..completion.isEqualTo('include: ')
..kind.isIdentifier,
(suggestion) => suggestion
..completion.isEqualTo('linter: ')
..kind.isIdentifier,
]);
}
Future<void> test_yaml_fixData_root() async {
await _configureWithWorkspaceRoot();
var path = convertPath('$testPackageRootPath/fix_data.yaml');
var response = await _getCodeSuggestions(
path: path,
content: '^',
);
check(response)
..assertComplete()
..hasEmptyReplacement();
check(response).suggestions.matches([
(suggestion) => suggestion
..completion.isEqualTo('version: ')
..kind.isIdentifier,
(suggestion) => suggestion
..completion.isEqualTo('transforms:')
..kind.isIdentifier,
]);
}
Future<void> test_yaml_pubspec_root() async {
await _configureWithWorkspaceRoot();
var path = convertPath('$testPackageRootPath/pubspec.yaml');
var response = await _getCodeSuggestions(
path: path,
content: '^',
);
check(response)
..assertComplete()
..hasEmptyReplacement();
check(response).suggestions.includesAll([
(suggestion) => suggestion
..completion.isEqualTo('name: ')
..kind.isIdentifier,
(suggestion) => suggestion
..completion.isEqualTo('dependencies: ')
..kind.isIdentifier,
(suggestion) => suggestion
..completion.isEqualTo('dev_dependencies: ')
..kind.isIdentifier,
]);
}
Future<CompletionResponseForTesting> _getCodeSuggestions({
required String path,
required String content,
int maxResults = 1 << 10,
}) async {
var completionOffset = content.indexOf('^');
expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
var nextOffset = content.indexOf('^', completionOffset + 1);
expect(nextOffset, equals(-1), reason: 'too many ^');
newFile(path,
content: content.substring(0, completionOffset) +
content.substring(completionOffset + 1));
return await _getSuggestions(
path: path,
completionOffset: completionOffset,
maxResults: maxResults,
);
}
Future<CompletionResponseForTesting> _getSuggestions({
required String path,
required int completionOffset,
required int maxResults,
}) async {
var request = CompletionGetSuggestions2Params(
path,
completionOffset,
maxResults,
).toRequest('0');
var response = await _handleSuccessfulRequest(request);
var result = CompletionGetSuggestions2Result.fromResponse(response);
return CompletionResponseForTesting(
requestOffset: completionOffset,
replacementOffset: result.replacementOffset,
replacementLength: result.replacementLength,
isIncomplete: result.isIncomplete,
suggestions: result.suggestions,
);
}
Future<CompletionResponseForTesting> _getTestCodeSuggestions(
String content, {
int maxResults = 1 << 10,
}) {
return _getCodeSuggestions(
path: convertPath(testFilePath),
content: content,
maxResults: maxResults,
);
}
RequestWithFutureResponse _sendTestCompletionRequest(String id, int offset) {
var request = CompletionGetSuggestions2Params(
testFilePathPlatform,
0,
1 << 10,
).toRequest(id);
var futureResponse = handleRequest(request);
return RequestWithFutureResponse(offset, request, futureResponse);
}
}
@reflectiveTest
class CompletionDomainHandlerGetSuggestionsTest
extends AbstractCompletionDomainTest {
Future<void> test_ArgumentList_constructor_named_fieldFormalParam() async {
// https://github.com/dart-lang/sdk/issues/31023
addTestFile('''
void f() { new A(field: ^);}
class A {
A({this.field: -1}) {}
}
''');
await getSuggestions();
}
Future<void> test_ArgumentList_constructor_named_param_label() async {
addTestFile('void f() { new A(^);}'
'class A { A({one, two}) {} }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ');
expect(suggestions, hasLength(2));
}
Future<void> test_ArgumentList_factory_named_param_label() async {
addTestFile('void f() { new A(^);}'
'class A { factory A({one, two}) => throw 0; }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ');
expect(suggestions, hasLength(2));
}
Future<void>
test_ArgumentList_function_named_fromPositionalNumeric_withoutSpace() async {
addTestFile('void f(int a, {int b = 0}) {}'
'void g() { f(2, ^3); }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'b: ');
expect(suggestions, hasLength(1));
// Ensure we don't try to replace the following arg.
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
}
Future<void>
test_ArgumentList_function_named_fromPositionalNumeric_withSpace() async {
addTestFile('void f(int a, {int b = 0}) {}'
'void g() { f(2, ^ 3); }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'b: ');
expect(suggestions, hasLength(1));
// Ensure we don't try to replace the following arg.
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
}
Future<void>
test_ArgumentList_function_named_fromPositionalVariable_withoutSpace() async {
addTestFile('void f(int a, {int b = 0}) {}'
'var foo = 1;'
'void g() { f(2, ^foo); }');
await getSuggestions();
expect(suggestions, hasLength(1));
// The named arg "b: " should not replace anything.
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'b: ',
replacementOffset: null, replacementLength: 0);
}
Future<void>
test_ArgumentList_function_named_fromPositionalVariable_withSpace() async {
addTestFile('void f(int a, {int b = 0}) {}'
'var foo = 1;'
'void g() { f(2, ^ foo); }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'b: ');
expect(suggestions, hasLength(1));
// Ensure we don't try to replace the following arg.
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
}
Future<void> test_ArgumentList_function_named_partiallyTyped() async {
addTestFile('''
class C {
void m(String firstString, {String secondString}) {}
void n() {
m('a', se^'b');
}
}
''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'secondString: ');
expect(suggestions, hasLength(1));
// Ensure we replace the correct section.
expect(replacementOffset, equals(completionOffset - 2));
expect(replacementLength, equals(2));
}
Future<void> test_ArgumentList_imported_function_named_param() async {
addTestFile('void f() { int.parse("16", ^);}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix: ');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError: ');
expect(suggestions, hasLength(2));
}
Future<void> test_ArgumentList_imported_function_named_param1() async {
addTestFile('void f() { foo(o^);} foo({one, two}) {}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'one: ');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ');
expect(suggestions, hasLength(2));
}
Future<void> test_ArgumentList_imported_function_named_param2() async {
addTestFile('void f() {A a = new A(); a.foo(one: 7, ^);}'
'class A { foo({one, two}) {} }');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'two: ');
expect(suggestions, hasLength(1));
}
Future<void> test_ArgumentList_imported_function_named_param_label1() async {
addTestFile('void f() { int.parse("16", r^: 16);}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError');
expect(suggestions, hasLength(2));
}
Future<void> test_ArgumentList_imported_function_named_param_label3() async {
addTestFile('void f() { int.parse("16", ^: 16);}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'radix');
assertHasResult(CompletionSuggestionKind.NAMED_ARGUMENT, 'onError');
expect(suggestions, hasLength(2));
}
Future<void> test_catch() async {
addTestFile('void f() {try {} ^}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'finally');
expect(suggestions, hasLength(3));
}
Future<void> test_catch2() async {
addTestFile('void f() {try {} on Foo {} ^}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'finally');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for');
suggestions.firstWhere(
(CompletionSuggestion suggestion) =>
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
fail('Expected suggestions other than keyword suggestions');
});
}
Future<void> test_catch3() async {
addTestFile('void f() {try {} catch (e) {} finally {} ^}');
await getSuggestions();
assertNoResult('on');
assertNoResult('catch');
assertNoResult('finally');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for');
suggestions.firstWhere(
(CompletionSuggestion suggestion) =>
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
fail('Expected suggestions other than keyword suggestions');
});
}
Future<void> test_catch4() async {
addTestFile('void f() {try {} finally {} ^}');
await getSuggestions();
assertNoResult('on');
assertNoResult('catch');
assertNoResult('finally');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'for');
suggestions.firstWhere(
(CompletionSuggestion suggestion) =>
suggestion.kind != CompletionSuggestionKind.KEYWORD, orElse: () {
fail('Expected suggestions other than keyword suggestions');
});
}
Future<void> test_catch5() async {
addTestFile('void f() {try {} ^ finally {}}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'on');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'catch');
expect(suggestions, hasLength(2));
}
Future<void> test_constructor() async {
addTestFile('class A {bool foo; A() : ^;}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_constructor2() async {
addTestFile('class A {bool foo; A() : s^;}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_constructor3() async {
addTestFile('class A {bool foo; A() : a=7,^;}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_constructor4() async {
addTestFile('class A {bool foo; A() : a=7,s^;}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_constructor5() async {
addTestFile('class A {bool foo; A() : a=7,s^}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_constructor6() async {
addTestFile('class A {bool foo; A() : a=7,^ void bar() {}}');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.KEYWORD, 'super');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
}
Future<void> test_extension() async {
addTestFile('''
class MyClass {
void foo() {
ba^
}
}
extension MyClassExtension on MyClass {
void bar() {}
}
''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.INVOCATION, 'bar');
}
Future<void> test_html() {
//
// We no longer support the analysis of non-dart files.
//
testFile = convertPath('/project/web/test.html');
addTestFile('''
<html>^</html>
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
expect(suggestions, hasLength(0));
});
}
Future<void> test_import_uri_with_trailing() {
final filePath = '/project/bin/testA.dart';
final incompleteImportText = toUriStr('/project/bin/t');
newFile(filePath, content: 'library libA;');
addTestFile('''
import "$incompleteImportText^.dart";
void f() {}''');
return getSuggestions().then((_) {
expect(replacementOffset,
equals(completionOffset - incompleteImportText.length));
expect(replacementLength, equals(5 + incompleteImportText.length));
assertHasResult(CompletionSuggestionKind.IMPORT, toUriStr(filePath));
assertNoResult('test');
});
}
Future<void> test_imports() {
addTestFile('''
import 'dart:html';
void f() {^}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'Object',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'HtmlElement',
elementKind: ElementKind.CLASS);
assertNoResult('test');
});
}
Future<void> test_imports_aborted_new_request() async {
addTestFile('''
class foo { }
c^''');
// Make a request for suggestions
var request1 = CompletionGetSuggestionsParams(testFile, completionOffset)
.toRequest('7');
var responseFuture1 = waitResponse(request1);
// Make another request before the first request completes
var request2 = CompletionGetSuggestionsParams(testFile, completionOffset)
.toRequest('8');
var responseFuture2 = waitResponse(request2);
// Await first response
var response1 = await responseFuture1;
var result1 = CompletionGetSuggestionsResult.fromResponse(response1);
assertValidId(result1.id);
// Await second response
var response2 = await responseFuture2;
var result2 = CompletionGetSuggestionsResult.fromResponse(response2);
assertValidId(result2.id);
// Wait for all processing to be complete
await analysisHandler.server.onAnalysisComplete;
await pumpEventQueue();
// Assert that first request has been aborted
expect(allSuggestions[result1.id], hasLength(0));
// Assert valid results for the second request
expect(allSuggestions[result2.id], same(suggestions));
assertHasResult(CompletionSuggestionKind.KEYWORD, 'class');
}
@failingTest
Future<void> test_imports_aborted_source_changed() async {
// TODO(brianwilkerson) Figure out whether this test makes sense when
// running the new driver. It waits for an initial empty notification then
// waits for a new notification. But I think that under the driver we only
// ever send one notification.
addTestFile('''
class foo { }
c^''');
// Make a request for suggestions
var request = CompletionGetSuggestionsParams(testFile, completionOffset)
.toRequest('0');
var responseFuture = waitResponse(request);
// Simulate user deleting text after request but before suggestions returned
server.updateContent('uc1', {testFile: AddContentOverlay(testCode)});
server.updateContent('uc2', {
testFile: ChangeContentOverlay([SourceEdit(completionOffset - 1, 1, '')])
});
// Await a response
var response = await responseFuture;
completionId = response.id;
assertValidId(completionId);
// Wait for all processing to be complete
await analysisHandler.server.onAnalysisComplete;
await pumpEventQueue();
// Assert that request has been aborted
expect(suggestionsDone, isTrue);
expect(suggestions, hasLength(0));
}
Future<void> test_imports_incremental() async {
addTestFile('''library foo;
e^
import "dart:async";
import "package:foo/foo.dart";
class foo { }''');
await waitForTasksFinished();
server.updateContent('uc1', {testFile: AddContentOverlay(testCode)});
server.updateContent('uc2', {
testFile: ChangeContentOverlay([SourceEdit(completionOffset, 0, 'xp')])
});
completionOffset += 2;
await getSuggestions();
expect(replacementOffset, completionOffset - 3);
expect(replacementLength, 3);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
selectionOffset: 8);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'import \'\';',
selectionOffset: 8);
assertNoResult('extends');
assertNoResult('library');
}
Future<void> test_imports_partial() async {
addTestFile('''^
import "package:foo/foo.dart";
import "package:bar/bar.dart";
class Baz { }''');
// Wait for analysis then edit the content
await waitForTasksFinished();
var revisedContent = testCode.substring(0, completionOffset) +
'i' +
testCode.substring(completionOffset);
++completionOffset;
server.handleRequest(AnalysisUpdateContentParams(
{testFile: AddContentOverlay(revisedContent)}).toRequest('add1'));
// Request code completion immediately after edit
var response = await waitResponse(
CompletionGetSuggestionsParams(testFile, completionOffset)
.toRequest('0'));
completionId = response.id;
assertValidId(completionId);
await waitForTasksFinished();
// wait for response to arrive
// because although the analysis is complete (waitForTasksFinished)
// the response may not yet have been processed
while (replacementOffset == null) {
await Future.delayed(Duration(milliseconds: 5));
}
expect(replacementOffset, completionOffset - 1);
expect(replacementLength, 1);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'library');
assertHasResult(CompletionSuggestionKind.KEYWORD, 'import \'\';',
selectionOffset: 8);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
selectionOffset: 8);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'part \'\';',
selectionOffset: 6);
assertNoResult('extends');
}
Future<void> test_imports_prefixed() {
addTestFile('''
import 'dart:html' as foo;
void f() {^}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'Object',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
assertNoResult('HtmlElement');
assertNoResult('test');
});
}
Future<void> test_imports_prefixed2() {
addTestFile('''
import 'dart:html' as foo;
void f() {foo.^}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'HtmlElement');
assertNoResult('test');
});
}
Future<void> test_inComment_block_beforeDartDoc() async {
addTestFile('''
/* text ^ */
/// some doc comments
class SomeClass {}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_block_beforeNode() async {
addTestFile('''
void f(aaa, bbb) {
/* text ^ */
print(42);
}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_endOfFile_withNewline() async {
addTestFile('''
// text ^
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_endOfFile_withoutNewline() async {
addTestFile('// text ^');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_endOfLine_beforeDartDoc() async {
addTestFile('''
// text ^
/// some doc comments
class SomeClass {}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_endOfLine_beforeNode() async {
addTestFile('''
void f(aaa, bbb) {
// text ^
print(42);
}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inComment_endOfLine_beforeToken() async {
addTestFile('''
void f(aaa, bbb) {
// text ^
}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inDartDoc1() async {
addTestFile('''
/// ^
void f(aaa, bbb) {}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inDartDoc2() async {
addTestFile('''
/// Some text^
void f(aaa, bbb) {}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inDartDoc3() async {
addTestFile('''
class MyClass {
/// ^
void foo() {}
void bar() {}
}
extension MyClassExtension on MyClass {
void baz() {}
}
''');
await getSuggestions();
expect(suggestions, isEmpty);
}
Future<void> test_inDartDoc_reference1() async {
newFile('/testA.dart', content: '''
part of libA;
foo(bar) => 0;''');
addTestFile('''
library libA;
part "${toUriStr('/testA.dart')}";
import "dart:math";
/// The [^]
void f(aaa, bbb) {}
''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'f');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'foo');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'min');
}
Future<void> test_inDartDoc_reference2() async {
addTestFile('''
/// The [m^]
void f(aaa, bbb) {}
''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'f');
}
Future<void> test_inDartDoc_reference3() async {
addTestFile('''
class MyClass {
/// [^]
void foo() {}
void bar() {}
}
extension MyClassExtension on MyClass {
void baz() {}
}
''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'bar');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'baz');
}
Future<void> test_inherited() {
addTestFile('''
class A {
m() {}
}
class B extends A {
x() {^}
}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm');
});
}
Future<void> test_invalidFilePathFormat_notAbsolute() async {
var request = CompletionGetSuggestionsParams('test.dart', 0).toRequest('0');
var response = await waitResponse(request);
expect(
response,
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
);
}
Future<void> test_invalidFilePathFormat_notNormalized() async {
var request =
CompletionGetSuggestionsParams(convertPath('/foo/../bar/test.dart'), 0)
.toRequest('0');
var response = await waitResponse(request);
expect(
response,
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
);
}
Future<void> test_invocation() {
addTestFile('class A {b() {}} void f() {A a; a.^}');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b');
});
}
Future<void> test_invocation_withTrailingStmt() {
addTestFile('class A {b() {}} void f() {A a; a.^ int x = 7;}');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b');
});
}
Future<void> test_is_asPrefixedIdentifierStart() async {
addTestFile('''
class A { var isVisible;}
void f(A p) { var v1 = p.is^; }''');
await getSuggestions();
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'isVisible');
}
Future<void> test_keyword() {
addTestFile('library A; cl^');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset - 2));
expect(replacementLength, equals(2));
assertHasResult(CompletionSuggestionKind.KEYWORD, 'export \'\';',
selectionOffset: 8);
assertHasResult(CompletionSuggestionKind.KEYWORD, 'class');
});
}
Future<void> test_local_implicitCreation() async {
addTestFile('''
class A {
A();
A.named();
}
void f() {
^
}
''');
await getSuggestions();
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
// The class is suggested.
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'A',
elementKind: ElementKind.CLASS);
// Both constructors - default and named, are suggested.
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A',
elementKind: ElementKind.CONSTRUCTOR);
assertHasResult(CompletionSuggestionKind.INVOCATION, 'A.named',
elementKind: ElementKind.CONSTRUCTOR);
}
Future<void> test_local_named_constructor() {
addTestFile('class A {A.c(); x() {new A.^}}');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'c');
assertNoResult('A');
});
}
Future<void> test_local_override() {
newFile('/project/bin/a.dart', content: 'class A {m() {}}');
addTestFile('''
import 'a.dart';
class B extends A {
m() {}
x() {^}
}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm');
});
}
Future<void> test_local_shadowClass() async {
addTestFile('''
class A {
A();
A.named();
}
void f() {
int A = 0;
^
}
''');
await getSuggestions();
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
// The class is suggested.
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'A');
// Class and all its constructors are shadowed by the local variable.
assertNoResult('A', elementKind: ElementKind.CLASS);
assertNoResult('A', elementKind: ElementKind.CONSTRUCTOR);
assertNoResult('A.named', elementKind: ElementKind.CONSTRUCTOR);
}
Future<void> test_locals() {
addTestFile('class A {var a; x() {var b;^}} class DateTime { }');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'A',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'a');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'b');
assertHasResult(CompletionSuggestionKind.INVOCATION, 'x');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'DateTime',
elementKind: ElementKind.CLASS);
});
}
Future<void> test_offset_past_eof() async {
addTestFile('void f() { }', offset: 300);
var request = CompletionGetSuggestionsParams(testFile, completionOffset)
.toRequest('0');
var response = await waitResponse(request);
expect(response.id, '0');
expect(response.error!.code, RequestErrorCode.INVALID_PARAMETER);
}
Future<void> test_overrides() {
newFile('/project/bin/a.dart', content: 'class A {m() {}}');
addTestFile('''
import 'a.dart';
class B extends A {m() {^}}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'm');
});
}
Future<void> test_partFile() {
newFile('/project/bin/a.dart', content: '''
library libA;
import 'dart:html';
part 'test.dart';
class A { }
''');
addTestFile('''
part of libA;
void f() {^}''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'Object',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'HtmlElement',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'A',
elementKind: ElementKind.CLASS);
assertNoResult('test');
});
}
Future<void> test_partFile2() {
newFile('/project/bin/a.dart', content: '''
part of libA;
class A { }''');
addTestFile('''
library libA;
part "a.dart";
import 'dart:html';
void f() {^}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'Object',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'HtmlElement',
elementKind: ElementKind.CLASS);
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'A',
elementKind: ElementKind.CLASS);
assertNoResult('test');
});
}
Future<void> test_sentToPlugins() async {
addTestFile('''
void f() {
^
}
''');
PluginInfo info = DiscoveredPluginInfo('a', 'b', 'c',
TestNotificationManager(), InstrumentationService.NULL_SERVICE);
var result = plugin.CompletionGetSuggestionsResult(
testFile.indexOf('^'), 0, <CompletionSuggestion>[
CompletionSuggestion(CompletionSuggestionKind.IDENTIFIER,
DART_RELEVANCE_DEFAULT, 'plugin completion', 3, 0, false, false)
]);
pluginManager.broadcastResults = <PluginInfo, Future<plugin.Response>>{
info: Future.value(result.toResponse('-', 1))
};
await getSuggestions();
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'plugin completion',
selectionOffset: 3);
}
Future<void> test_simple() {
addTestFile('''
void f() {
^
}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'Object',
elementKind: ElementKind.CLASS);
assertNoResult('HtmlElement');
assertNoResult('test');
});
}
Future<void> test_static() async {
addTestFile('class A {static b() {} c() {}} void f() {A.^}');
await getSuggestions();
expect(replacementOffset, equals(completionOffset));
expect(replacementLength, equals(0));
assertHasResult(CompletionSuggestionKind.INVOCATION, 'b');
assertNoResult('c');
}
Future<void> test_topLevel() {
addTestFile('''
typedef foo();
var test = '';
void f() {tes^t}
''');
return getSuggestions().then((_) {
expect(replacementOffset, equals(completionOffset - 3));
expect(replacementLength, equals(4));
// Suggestions based upon imported elements are partially filtered
//assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
assertHasResult(CompletionSuggestionKind.IDENTIFIER, 'test');
assertNoResult('HtmlElement');
});
}
}
class PubPackageAnalysisServerTest with ResourceProviderMixin {
late final MockServerChannel serverChannel;
late final AnalysisServer server;
AnalysisDomainHandler get analysisDomain {
return server.handlers.whereType<AnalysisDomainHandler>().single;
}
CompletionDomainHandler get completionDomain {
return server.handlers.whereType<CompletionDomainHandler>().single;
}
List<String> get experiments => [
EnableString.enhanced_enums,
EnableString.named_arguments_anywhere,
EnableString.super_parameters,
];
String get testFileContent => getFile(testFilePath).readAsStringSync();
String get testFilePath => '$testPackageLibPath/test.dart';
String get testFilePathPlatform => convertPath(testFilePath);
String get testPackageLibPath => '$testPackageRootPath/lib';
Folder get testPackageRoot => getFolder(testPackageRootPath);
String get testPackageRootPath => '$workspaceRootPath/test';
String get testPackageTestPath => '$testPackageRootPath/test';
String get workspaceRootPath => '/home';
void deleteTestPackageAnalysisOptionsFile() {
deleteAnalysisOptionsYamlFile(testPackageRootPath);
}
void deleteTestPackageConfigJsonFile() {
deletePackageConfigJsonFile(testPackageRootPath);
}
Future<Response> handleRequest(Request request) async {
return await serverChannel.sendRequest(request);
}
void processNotification(Notification notification) {}
Future<void> setRoots({
required List<String> included,
required List<String> excluded,
}) async {
var includedConverted = included.map(convertPath).toList();
var excludedConverted = excluded.map(convertPath).toList();
await _handleSuccessfulRequest(
AnalysisSetAnalysisRootsParams(
includedConverted,
excludedConverted,
packageRoots: {},
).toRequest('0'),
);
}
@mustCallSuper
void setUp() {
serverChannel = MockServerChannel();
var sdkRoot = newFolder('/sdk');
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
);
writeTestPackageConfig();
writeTestPackageAnalysisOptionsFile(
AnalysisOptionsFileConfig(
experiments: experiments,
),
);
serverChannel.notifications.listen(processNotification);
server = AnalysisServer(
serverChannel,
resourceProvider,
AnalysisServerOptions(),
DartSdkManager(sdkRoot.path),
CrashReportingAttachmentsBuilder.empty,
InstrumentationService.NULL_SERVICE,
);
completionDomain.budgetDuration = const Duration(seconds: 30);
}
void writePackageConfig(Folder root, PackageConfigFileBuilder config) {
newPackageConfigJsonFile(
root.path,
content: config.toContent(toUriStr: toUriStr),
);
}
void writeTestPackageAnalysisOptionsFile(AnalysisOptionsFileConfig config) {
newAnalysisOptionsYamlFile(
testPackageRootPath,
content: config.toContent(),
);
}
void writeTestPackageConfig({
PackageConfigFileBuilder? config,
String? languageVersion,
}) {
if (config == null) {
config = PackageConfigFileBuilder();
} else {
config = config.copy();
}
config.add(
name: 'test',
rootPath: testPackageRootPath,
languageVersion: languageVersion,
);
writePackageConfig(testPackageRoot, config);
}
void writeTestPackagePubspecYamlFile(String content) {
newPubspecYamlFile(testPackageRootPath, content);
}
Future<void> _configureWithWorkspaceRoot() async {
await setRoots(included: [workspaceRootPath], excluded: []);
await server.onAnalysisComplete;
}
/// Validates that the given [request] is handled successfully.
Future<Response> _handleSuccessfulRequest(Request request) async {
var response = await handleRequest(request);
expect(response, isResponseSuccess(request.id));
return response;
}
}
class RequestWithFutureResponse {
final int offset;
final Request request;
final Future<Response> futureResponse;
RequestWithFutureResponse(this.offset, this.request, this.futureResponse);
Future<CompletionResponseForTesting> toResponse() async {
var response = await futureResponse;
expect(response, isResponseSuccess(request.id));
var result = CompletionGetSuggestions2Result.fromResponse(response);
return CompletionResponseForTesting(
requestOffset: offset,
replacementOffset: result.replacementOffset,
replacementLength: result.replacementLength,
isIncomplete: result.isIncomplete,
suggestions: result.suggestions,
);
}
}
extension on CheckTarget<CompletionGetSuggestionDetails2Result> {
@useResult
CheckTarget<SourceChange> get change {
return nest(
value.change,
(selected) => 'has change ${valueStr(selected)}',
);
}
@useResult
CheckTarget<String> get completion {
return nest(
value.completion,
(selected) => 'has completion ${valueStr(selected)}',
);
}
}