Index: pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
diff --git a/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..18b8794e4a10061d9b0de3dc9616f5a097bf52fd |
--- /dev/null |
+++ b/pkg/analysis_server/test/services/completion/statement/statement_completion_test.dart |
@@ -0,0 +1,274 @@ |
+// Copyright (c) 2017, 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.statement; |
+ |
+import 'package:analysis_server/src/protocol_server.dart'; |
+import 'package:analysis_server/src/services/completion/statement/statement_completion.dart'; |
+import 'package:analyzer/src/dart/analysis/driver.dart'; |
+import 'package:test/test.dart'; |
+import 'package:test_reflective_loader/test_reflective_loader.dart'; |
+ |
+import '../../../abstract_single_unit.dart'; |
+ |
+main() { |
+ defineReflectiveSuite(() { |
+ defineReflectiveTests(StatementCompletionTest); |
+ }); |
+} |
+ |
+@reflectiveTest |
+class StatementCompletionTest extends AbstractSingleUnitTest { |
+ SourceChange change; |
+ |
+ bool get enableNewAnalysisDriver => true; |
+ |
+ test_completeIfEmptyCondition() async { |
+ await _prepareCompletion( |
+ 'if ()', |
+ ''' |
+main() { |
+ if () |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if () { |
+ //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf('if (') + 'if ('.length); |
+ } |
+ |
+ test_completeIfKeywordOnly() async { |
+ await _prepareCompletion( |
+ 'if', |
+ ''' |
+main() { |
+ if //// |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if () { |
+ //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf('if (') + 'if ('.length); |
+ } |
+ |
+ test_completeIfWithCondition() async { |
+ await _prepareCompletion( |
+ 'if (tr', // Trigger completion from within expression. |
+ ''' |
+main() { |
+ if (true) |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if (true) { |
+ //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf(' ') + ' '.length); |
+ } |
+ |
+ test_completeIfAfterCondition_BAD() async { |
+ // TODO(messick): Fix the code to make this like test_completeIfWithCondition. |
+ // Recap: Finding the node at the selectionOffset returns the block, not the |
+ // if-statement. Need to understand if that only happens when the if-statement |
+ // is the only statement in the block, or perhaps first or last? And what |
+ // happens when it is in the middle of other statements? |
+ await _prepareCompletion( |
+ 'if (true) ', // Trigger completion after space. |
+ ''' |
+main() { |
+ if (true) //// |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ // Note: This is not what we want. |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ if (true) //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf('if (true) ') + 'if (true) '.length); |
+ } |
+ |
+ test_completeIfWithElse_BAD() async { |
+ await _prepareCompletion( |
+ 'if ()', |
+ ''' |
+main() { |
+ if () |
+ else |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ // Note: if-statement completion should not trigger. |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ if () |
+ else |
+ } |
+} |
+''', |
+ (s) => s.indexOf('if ()') + 'if ()'.length); |
+ } |
+ |
+ test_completeIfWithinEmptyCondition() async { |
+ await _prepareCompletion( |
+ 'if (', |
+ ''' |
+main() { |
+ if () |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete if-statement', |
+ ''' |
+main() { |
+ if () { |
+ //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf('if (') + 'if ('.length); |
+ } |
+ |
+ test_completeWhileKeywordOnly() async { |
+ await _prepareCompletion( |
+ 'while', |
+ ''' |
+main() { |
+ while //// |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Complete while-statement', |
+ ''' |
+main() { |
+ while () { |
+ //// |
+ } |
+} |
+''', |
+ (s) => s.indexOf('while (') + 'while ('.length); |
+ } |
+ |
+ test_simpleEnter() async { |
+ await _prepareCompletion( |
+ 'v = 1;', |
+ ''' |
+main() { |
+ int v = 1; |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Insert a newline at the end of the current line', |
+ ''' |
+main() { |
+ int v = 1; |
+ //// |
+} |
+'''); |
+ } |
+ |
+ test_simpleSemicolon() async { |
+ await _prepareCompletion( |
+ 'v = 1', |
+ ''' |
+main() { |
+ int v = 1 |
+} |
+''', |
+ atEnd: true); |
+ _assertHasChange( |
+ 'Add a semicolon and newline', |
+ ''' |
+main() { |
+ int v = 1; |
+ //// |
+} |
+''', |
+ (s) => s.lastIndexOf(' ') + ' '.length); |
+ } |
+ |
+ void _assertHasChange(String message, String expectedCode, [Function cmp]) { |
+ if (change.message == message) { |
+ if (!change.edits.isEmpty) { |
+ String resultCode = |
+ SourceEdit.applySequence(testCode, change.edits[0].edits); |
+ expect(resultCode, expectedCode.replaceAll('////', '')); |
+ if (cmp != null) { |
+ int offset = cmp(resultCode); |
+ expect(change.selection.offset, offset); |
+ } |
+ } else { |
+ if (cmp != null) { |
+ int offset = cmp(testCode); |
+ expect(change.selection.offset, offset); |
+ } |
+ } |
+ return; |
+ } |
+ fail("Expected to find |$message| but got: " + change.message); |
+ } |
+ |
+ _computeCompletion(int offset) async { |
+ driver.changeFile(testFile); |
+ AnalysisResult result = await driver.getResult(testFile); |
+ StatementCompletionContext context = new StatementCompletionContext( |
+ testFile, |
+ result.lineInfo, |
+ offset, |
+ testUnit, |
+ testUnitElement, |
+ result.errors); |
+ StatementCompletionProcessor processor = |
+ new StatementCompletionProcessor(context); |
+ StatementCompletion completion = await processor.compute(); |
+ change = completion.change; |
+ } |
+ |
+ _prepareCompletion(String search, String sourceCode, |
+ {bool atStart: false, bool atEnd: false, int delta: 0}) async { |
+ testCode = sourceCode.replaceAll('////', ''); |
+ int offset = findOffset(search); |
+ if (atStart) { |
+ delta = 0; |
+ } else if (atEnd) { |
+ delta = search.length; |
+ } |
+ await _prepareCompletionAt(offset + delta, testCode); |
+ } |
+ |
+ _prepareCompletionAt(int offset, String sourceCode) async { |
+ verifyNoTestUnitErrors = false; |
+ await resolveTestUnit(sourceCode); |
+ await _computeCompletion(offset); |
+ } |
+} |