| // Copyright (c) 2018, 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/domains/execution/completion.dart'; | |
| import 'package:analysis_server/src/protocol_server.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../../../abstract_context.dart'; | |
| main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(RuntimeCompletionComputerTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class RuntimeCompletionComputerTest extends AbstractContextTest { | |
| String contextFile; | |
| int contextOffset; | |
| RuntimeCompletionResult result; | |
| void addContextFile(String content) { | |
| contextFile = convertPath('/test/lib/context.dart'); | |
| addSource(contextFile, content); | |
| contextOffset = content.indexOf('// context line'); | |
| expect(contextOffset, isNonNegative, | |
| reason: "Not found '// context line'."); | |
| } | |
| void assertNotSuggested(String completion) { | |
| CompletionSuggestion suggestion = getSuggest(completion); | |
| if (suggestion != null) { | |
| failedCompletion('unexpected $completion'); | |
| } | |
| } | |
| void assertSuggested(String completion, {String returnType}) { | |
| CompletionSuggestion suggestion = getSuggest(completion); | |
| if (suggestion == null) { | |
| failedCompletion('expected $completion'); | |
| } | |
| if (returnType != null) { | |
| expect(suggestion.returnType, returnType); | |
| } | |
| } | |
| Future<void> computeCompletion( | |
| String code, { | |
| List<RuntimeCompletionVariable> variables, | |
| List<RuntimeCompletionExpression> expressions, | |
| }) async { | |
| int codeOffset = code.indexOf('^'); | |
| expect(codeOffset, isNonNegative); | |
| code = code.replaceAll('^', ''); | |
| var computer = new RuntimeCompletionComputer( | |
| resourceProvider, | |
| fileContentOverlay, | |
| driver, | |
| code, | |
| codeOffset, | |
| contextFile, | |
| contextOffset, | |
| variables, | |
| expressions); | |
| result = await computer.compute(); | |
| } | |
| void failedCompletion(String message) { | |
| var sb = new StringBuffer(message); | |
| if (result.suggestions != null) { | |
| sb.write('\n found'); | |
| result.suggestions.toList() | |
| ..sort((a, b) => a.completion.compareTo(b.completion)) | |
| ..forEach((suggestion) { | |
| sb.write('\n ${suggestion.completion} -> $suggestion'); | |
| }); | |
| } | |
| fail(sb.toString()); | |
| } | |
| CompletionSuggestion getSuggest(String completion) { | |
| expect(result.suggestions, isNotNull); | |
| for (var suggestion in result.suggestions) { | |
| if (suggestion.completion == completion) { | |
| return suggestion; | |
| } | |
| } | |
| return null; | |
| } | |
| test_class_fields() async { | |
| addContextFile(r''' | |
| class A { | |
| int a; | |
| } | |
| class B extends A { | |
| double b, c; | |
| void foo() { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| assertSuggested('c', returnType: 'double'); | |
| } | |
| test_class_methods() async { | |
| addContextFile(r''' | |
| class A { | |
| int a() => null; | |
| } | |
| class B extends A { | |
| double b() => null; | |
| void foo() { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_inPart() async { | |
| addSource('/test/lib/a.dart', r''' | |
| part 'b.dart'; | |
| part 'context.dart'; | |
| int a; | |
| '''); | |
| addSource('/test/lib/b.dart', r''' | |
| part of 'a.dart'; | |
| double b; | |
| '''); | |
| addContextFile(r''' | |
| part of 'a.dart'; | |
| String c; | |
| void main() { | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| assertSuggested('c', returnType: 'String'); | |
| } | |
| test_locals_block() async { | |
| addContextFile(r''' | |
| class A { | |
| int foo; | |
| } | |
| void contextFunction() { | |
| var a = new A(); | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('a.^'); | |
| assertSuggested('foo'); | |
| // There was an issue with cleaning up | |
| // Check that the second time it works too. | |
| await computeCompletion('a.^'); | |
| assertSuggested('foo'); | |
| } | |
| test_locals_block_codeWithClosure() async { | |
| addContextFile(r''' | |
| main() { | |
| var items = <String>[]; | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('items.forEach((e) => e.^)'); | |
| assertSuggested('toUpperCase'); | |
| } | |
| test_locals_block_nested() async { | |
| addContextFile(r''' | |
| void main() { | |
| var a = 0; | |
| var b = 0.0; | |
| { | |
| var a = ''; | |
| // context line | |
| } | |
| var c = 0; | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'String'); | |
| assertSuggested('b', returnType: 'double'); | |
| // "c" is defined after the context offset, so is not visible. | |
| assertNotSuggested('c'); | |
| } | |
| test_locals_for() async { | |
| addContextFile(r''' | |
| void main(List<int> intItems, List<double> doubleItems) { | |
| for (var a = 0, b = 0.0; a < 5; a++) { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_locals_forEach() async { | |
| addContextFile(r''' | |
| void main(List<int> intItems, List<double> doubleItems) { | |
| for (var a in intItems) { | |
| for (var b in doubleItems) { | |
| // context line | |
| } | |
| } | |
| }sosol | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_parameters_constructor() async { | |
| addContextFile(r''' | |
| class C { | |
| C(int a, double b) { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_parameters_function() async { | |
| addContextFile(r''' | |
| void main(int a, double b) { | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_parameters_function_locals() async { | |
| addContextFile(r''' | |
| void main(int a, int b) { | |
| String a; | |
| double c; | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'String'); | |
| assertSuggested('b', returnType: 'int'); | |
| assertSuggested('c', returnType: 'double'); | |
| } | |
| test_parameters_function_nested() async { | |
| addContextFile(r''' | |
| void foo(int a, double b) { | |
| void bar(String a, bool c) { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'String'); | |
| assertSuggested('b', returnType: 'double'); | |
| assertSuggested('c', returnType: 'bool'); | |
| } | |
| test_parameters_functionExpression() async { | |
| addContextFile(r''' | |
| void main(List<int> intItems, List<double> doubleItems) { | |
| intItems.forEach((a) { | |
| doubleItems.forEach((b) { | |
| // context line | |
| }); | |
| }); | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_parameters_method() async { | |
| addContextFile(r''' | |
| class C { | |
| void main(int a, double b) { | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_parameters_method_locals() async { | |
| addContextFile(r''' | |
| class C { | |
| void main(int a, int b) { | |
| String a; | |
| double c; | |
| // context line | |
| } | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'String'); | |
| assertSuggested('b', returnType: 'int'); | |
| assertSuggested('c', returnType: 'double'); | |
| } | |
| test_syntheticImportPrefix() async { | |
| newFile('/test/lib/a.dart', content: 'class A {}'); | |
| newFile('/test/lib/b.dart', content: 'class B {}'); | |
| addContextFile(r''' | |
| import 'a.dart'; | |
| impoty 'b.dart'; | |
| main() { | |
| var a = new A(); | |
| var b = new B(); | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| for (var suggestion in result.suggestions) { | |
| expect(suggestion.completion, isNot(startsWith('__prefix'))); | |
| } | |
| } | |
| test_topLevelFunctions() async { | |
| addContextFile(r''' | |
| int a() => null; | |
| double b() => null; | |
| void main() { | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| test_topLevelVariables() async { | |
| addContextFile(r''' | |
| int a; | |
| double b; | |
| void main() { | |
| // context line | |
| } | |
| '''); | |
| await computeCompletion('^'); | |
| assertSuggested('a', returnType: 'int'); | |
| assertSuggested('b', returnType: 'double'); | |
| } | |
| } |