6de1c478c3
This reverts commit 3f8ea3f508.
Change-Id: Ib034895f4cf29492e66dd07b7da1a99ba5e94bf6
Reviewed-on: https://dart-review.googlesource.com/c/90885
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
2243 lines
50 KiB
Dart
2243 lines
50 KiB
Dart
// 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/protocol/protocol.dart';
|
|
import 'package:analysis_server/protocol/protocol_generated.dart';
|
|
import 'package:analysis_server/src/edit/edit_domain.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import '../analysis_abstract.dart';
|
|
import '../mocks.dart';
|
|
import '../src/utilities/flutter_util.dart' as flutter;
|
|
|
|
main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(ConvertGetterMethodToMethodTest);
|
|
defineReflectiveTests(ConvertMethodToGetterTest);
|
|
defineReflectiveTests(ExtractLocalVariableTest);
|
|
defineReflectiveTests(ExtractMethodTest);
|
|
defineReflectiveTests(GetAvailableRefactoringsTest);
|
|
defineReflectiveTests(InlineLocalTest);
|
|
defineReflectiveTests(InlineMethodTest);
|
|
defineReflectiveTests(MoveFileTest);
|
|
defineReflectiveTests(RenameTest);
|
|
});
|
|
}
|
|
|
|
/// Wrapper around the test package's `fail` function.
|
|
///
|
|
/// Unlike the test package's `fail` function, this function is not annotated
|
|
/// with @alwaysThrows, so we can call it at the top of a test method without
|
|
/// causing the rest of the method to be flagged as dead code.
|
|
void _fail(String message) {
|
|
fail(message);
|
|
}
|
|
|
|
@reflectiveTest
|
|
class ConvertGetterMethodToMethodTest extends _AbstractGetRefactoring_Test {
|
|
test_function() {
|
|
addTestFile('''
|
|
int get test => 42;
|
|
main() {
|
|
var a = 1 + test;
|
|
var b = 2 + test;
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendConvertRequest('test =>');
|
|
}, '''
|
|
int test() => 42;
|
|
main() {
|
|
var a = 1 + test();
|
|
var b = 2 + test();
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_init_fatalError_notExplicit() {
|
|
addTestFile('''
|
|
int test = 42;
|
|
main() {
|
|
var v = test;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return _sendConvertRequest('test;');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(result.initialProblems,
|
|
'Only explicit getters can be converted to methods.');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_method() {
|
|
addTestFile('''
|
|
class A {
|
|
int get test => 1;
|
|
}
|
|
class B extends A {
|
|
int get test => 2;
|
|
}
|
|
class C extends B {
|
|
int get test => 3;
|
|
}
|
|
class D extends A {
|
|
int get test => 4;
|
|
}
|
|
main(A a, B b, C c, D d) {
|
|
var va = a.test;
|
|
var vb = b.test;
|
|
var vc = c.test;
|
|
var vd = d.test;
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendConvertRequest('test => 2');
|
|
}, '''
|
|
class A {
|
|
int test() => 1;
|
|
}
|
|
class B extends A {
|
|
int test() => 2;
|
|
}
|
|
class C extends B {
|
|
int test() => 3;
|
|
}
|
|
class D extends A {
|
|
int test() => 4;
|
|
}
|
|
main(A a, B b, C c, D d) {
|
|
var va = a.test();
|
|
var vb = b.test();
|
|
var vc = c.test();
|
|
var vd = d.test();
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<Response> _sendConvertRequest(String search) {
|
|
Request request = new EditGetRefactoringParams(
|
|
RefactoringKind.CONVERT_GETTER_TO_METHOD,
|
|
testFile,
|
|
findOffset(search),
|
|
0,
|
|
false)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class ConvertMethodToGetterTest extends _AbstractGetRefactoring_Test {
|
|
test_function() {
|
|
addTestFile('''
|
|
int test() => 42;
|
|
main() {
|
|
var a = 1 + test();
|
|
var b = 2 + test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendConvertRequest('test() =>');
|
|
}, '''
|
|
int get test => 42;
|
|
main() {
|
|
var a = 1 + test;
|
|
var b = 2 + test;
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_init_fatalError_hasParameters() {
|
|
addTestFile('''
|
|
int test(p) => p + 1;
|
|
main() {
|
|
var v = test(2);
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return _sendConvertRequest('test(p)');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(result.initialProblems,
|
|
'Only methods without parameters can be converted to getters.');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_init_fatalError_notExecutableElement() {
|
|
addTestFile('''
|
|
main() {
|
|
int abc = 1;
|
|
print(abc);
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return _sendConvertRequest('abc');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(
|
|
result.initialProblems, 'Unable to create a refactoring');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_method() {
|
|
addTestFile('''
|
|
class A {
|
|
int test() => 1;
|
|
}
|
|
class B extends A {
|
|
int test() => 2;
|
|
}
|
|
class C extends B {
|
|
int test() => 3;
|
|
}
|
|
class D extends A {
|
|
int test() => 4;
|
|
}
|
|
main(A a, B b, C c, D d) {
|
|
var va = a.test();
|
|
var vb = b.test();
|
|
var vc = c.test();
|
|
var vd = d.test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendConvertRequest('test() => 2');
|
|
}, '''
|
|
class A {
|
|
int get test => 1;
|
|
}
|
|
class B extends A {
|
|
int get test => 2;
|
|
}
|
|
class C extends B {
|
|
int get test => 3;
|
|
}
|
|
class D extends A {
|
|
int get test => 4;
|
|
}
|
|
main(A a, B b, C c, D d) {
|
|
var va = a.test;
|
|
var vb = b.test;
|
|
var vc = c.test;
|
|
var vd = d.test;
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<Response> _sendConvertRequest(String search) {
|
|
Request request = new EditGetRefactoringParams(
|
|
RefactoringKind.CONVERT_METHOD_TO_GETTER,
|
|
testFile,
|
|
findOffset(search),
|
|
0,
|
|
false)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class ExtractLocalVariableTest extends _AbstractGetRefactoring_Test {
|
|
Future<Response> sendExtractRequest(
|
|
int offset, int length, String name, bool extractAll) {
|
|
RefactoringKind kind = RefactoringKind.EXTRACT_LOCAL_VARIABLE;
|
|
ExtractLocalVariableOptions options =
|
|
name != null ? new ExtractLocalVariableOptions(name, extractAll) : null;
|
|
return sendRequest(kind, offset, length, options, false);
|
|
}
|
|
|
|
Future<Response> sendStringRequest(
|
|
String search, String name, bool extractAll) {
|
|
int offset = findOffset(search);
|
|
int length = search.length;
|
|
return sendExtractRequest(offset, length, name, extractAll);
|
|
}
|
|
|
|
Future<Response> sendStringSuffixRequest(
|
|
String search, String suffix, String name, bool extractAll) {
|
|
int offset = findOffset(search + suffix);
|
|
int length = search.length;
|
|
return sendExtractRequest(offset, length, name, extractAll);
|
|
}
|
|
|
|
void tearDown() {
|
|
test_simulateRefactoringException_init = false;
|
|
test_simulateRefactoringException_final = false;
|
|
test_simulateRefactoringException_change = false;
|
|
super.tearDown();
|
|
}
|
|
|
|
test_analysis_onlyOneFile() async {
|
|
shouldWaitForFullAnalysis = false;
|
|
newFile(join(testFolder, 'other.dart'), content: r'''
|
|
foo(int myName) {}
|
|
''');
|
|
addTestFile('''
|
|
import 'other.dart';
|
|
main() {
|
|
foo(1 + 2);
|
|
}
|
|
''');
|
|
// Start refactoring.
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
});
|
|
// We get the refactoring feedback....
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.names, contains('myName'));
|
|
}
|
|
|
|
test_coveringExpressions() {
|
|
addTestFile('''
|
|
main() {
|
|
var v = 111 + 222 + 333;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendExtractRequest(testCode.indexOf('222 +'), 0, 'res', true);
|
|
}).then((result) {
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.coveringExpressionOffsets, [
|
|
testCode.indexOf('222 +'),
|
|
testCode.indexOf('111 +'),
|
|
testCode.indexOf('111 +')
|
|
]);
|
|
expect(feedback.coveringExpressionLengths,
|
|
['222'.length, '111 + 222'.length, '111 + 222 + 333'.length]);
|
|
});
|
|
}
|
|
|
|
test_extractAll() {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
}, '''
|
|
main() {
|
|
var res = 1 + 2;
|
|
print(res);
|
|
print(res);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_extractOne() {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
print(1 + 2); // marker
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendStringSuffixRequest('1 + 2', '); // marker', 'res', false);
|
|
}, '''
|
|
main() {
|
|
print(1 + 2);
|
|
var res = 1 + 2;
|
|
print(res); // marker
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_invalidFilePathFormat_notAbsolute() async {
|
|
var request = new EditGetRefactoringParams(
|
|
RefactoringKind.EXTRACT_LOCAL_VARIABLE, 'test.dart', 0, 0, true)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
test_invalidFilePathFormat_notNormalized() async {
|
|
var request = new EditGetRefactoringParams(
|
|
RefactoringKind.EXTRACT_LOCAL_VARIABLE,
|
|
convertPath('/foo/../bar/test.dart'),
|
|
0,
|
|
0,
|
|
true)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
test_names() async {
|
|
addTestFile('''
|
|
class TreeItem {}
|
|
TreeItem getSelectedItem() => null;
|
|
main() {
|
|
var a = getSelectedItem();
|
|
}
|
|
''');
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringSuffixRequest('getSelectedItem()', ';', null, true);
|
|
});
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(
|
|
feedback.names, unorderedEquals(['treeItem', 'item', 'selectedItem']));
|
|
expect(result.change, isNull);
|
|
}
|
|
|
|
test_nameWarning() async {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'Name', true);
|
|
});
|
|
assertResultProblemsWarning(result.optionsProblems,
|
|
'Variable name should start with a lowercase letter.');
|
|
// ...but there is still a change
|
|
assertTestRefactoringResult(result, '''
|
|
main() {
|
|
var Name = 1 + 2;
|
|
print(Name);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_offsetsLengths() {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
}).then((result) {
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.offsets, [findOffset('1 + 2'), findOffset('1 + 2')]);
|
|
expect(feedback.lengths, [5, 6]);
|
|
});
|
|
}
|
|
|
|
test_resetOnAnalysisSetChanged_overlay() async {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2); // 0
|
|
}
|
|
''');
|
|
|
|
Future<void> checkUpdate(doUpdate()) async {
|
|
await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
});
|
|
int initialResetCount = test_resetCount;
|
|
doUpdate();
|
|
await pumpEventQueue();
|
|
expect(test_resetCount, initialResetCount + 1);
|
|
}
|
|
|
|
await checkUpdate(() {
|
|
server.updateContent('u1', {
|
|
testFile: new AddContentOverlay('''
|
|
main() {
|
|
print(1 + 2); // 1
|
|
}
|
|
''')
|
|
});
|
|
});
|
|
|
|
await checkUpdate(() {
|
|
server.updateContent('u2', {
|
|
testFile: new ChangeContentOverlay([
|
|
new SourceEdit(0, 0, '''
|
|
main() {
|
|
print(1 + 2); // 2
|
|
}
|
|
''')
|
|
])
|
|
});
|
|
});
|
|
|
|
await checkUpdate(() {
|
|
server.updateContent('u3', {testFile: new RemoveContentOverlay()});
|
|
});
|
|
}
|
|
|
|
test_resetOnAnalysisSetChanged_watch_otherFile() async {
|
|
String otherFile = join(testFolder, 'other.dart');
|
|
newFile(otherFile, content: '// other 1');
|
|
addTestFile('''
|
|
main() {
|
|
foo(1 + 2);
|
|
}
|
|
foo(int myName) {}
|
|
''');
|
|
// Send the first request.
|
|
{
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
});
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.names, contains('myName'));
|
|
}
|
|
int initialResetCount = test_resetCount;
|
|
// Update the other.dart file.
|
|
// The refactoring is reset, even though it's a different file. It is up to
|
|
// analyzer to track dependencies and provide resolved units fast when
|
|
// possible.
|
|
newFile(otherFile, content: '// other 2');
|
|
await pumpEventQueue();
|
|
expect(test_resetCount, initialResetCount + 1);
|
|
}
|
|
|
|
test_resetOnAnalysisSetChanged_watch_thisFile() async {
|
|
addTestFile('''
|
|
main() {
|
|
foo(1 + 2);
|
|
}
|
|
foo(int myName) {}
|
|
''');
|
|
// Send the first request.
|
|
{
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
});
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.names, contains('myName'));
|
|
}
|
|
int initialResetCount = test_resetCount;
|
|
// Update the test.dart file.
|
|
modifyTestFile('''
|
|
main() {
|
|
foo(1 + 2);
|
|
}
|
|
foo(int otherName) {}
|
|
''');
|
|
// The refactoring was reset.
|
|
await pumpEventQueue();
|
|
expect(test_resetCount, initialResetCount + 1);
|
|
// Send the second request, with the same kind, file and offset.
|
|
{
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendStringRequest('1 + 2', 'res', true);
|
|
});
|
|
ExtractLocalVariableFeedback feedback = result.feedback;
|
|
// The refactoring was reset, so we don't get stale results.
|
|
expect(feedback.names, contains('otherName'));
|
|
}
|
|
}
|
|
|
|
test_serverError_change() {
|
|
test_simulateRefactoringException_change = true;
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendStringRequest('1 + 2', 'res', true).then((response) {
|
|
expect(response.error, isNotNull);
|
|
expect(response.error.code, RequestErrorCode.SERVER_ERROR);
|
|
});
|
|
});
|
|
}
|
|
|
|
test_serverError_final() {
|
|
test_simulateRefactoringException_final = true;
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendStringRequest('1 + 2', 'res', true).then((response) {
|
|
expect(response.error, isNotNull);
|
|
expect(response.error.code, RequestErrorCode.SERVER_ERROR);
|
|
});
|
|
});
|
|
}
|
|
|
|
test_serverError_init() {
|
|
test_simulateRefactoringException_init = true;
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendStringRequest('1 + 2', 'res', true).then((response) {
|
|
expect(response.error, isNotNull);
|
|
expect(response.error.code, RequestErrorCode.SERVER_ERROR);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class ExtractMethodTest extends _AbstractGetRefactoring_Test {
|
|
int offset;
|
|
int length;
|
|
String name = 'res';
|
|
ExtractMethodOptions options;
|
|
|
|
test_expression() {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('1 + 2');
|
|
return assertSuccessfulRefactoring(_computeChange, '''
|
|
main() {
|
|
print(res());
|
|
print(res());
|
|
}
|
|
|
|
int res() => 1 + 2;
|
|
''');
|
|
}
|
|
|
|
test_expression_hasParameters() {
|
|
addTestFile('''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
print(a + b);
|
|
print(a + b);
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('a + b');
|
|
return assertSuccessfulRefactoring(_computeChange, '''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
print(res(a, b));
|
|
print(res(a, b));
|
|
}
|
|
|
|
int res(int a, int b) => a + b;
|
|
''');
|
|
}
|
|
|
|
test_expression_updateParameters() {
|
|
addTestFile('''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
print(a + b);
|
|
print(a + b);
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('a + b');
|
|
return getRefactoringResult(_computeChange).then((result) {
|
|
ExtractMethodFeedback feedback = result.feedback;
|
|
List<RefactoringMethodParameter> parameters = feedback.parameters;
|
|
parameters[0].name = 'aaa';
|
|
parameters[1].name = 'bbb';
|
|
parameters[1].type = 'num';
|
|
parameters.insert(0, parameters.removeLast());
|
|
options.parameters = parameters;
|
|
return assertSuccessfulRefactoring(_sendExtractRequest, '''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
print(res(b, a));
|
|
print(res(b, a));
|
|
}
|
|
|
|
int res(num bbb, int aaa) => aaa + bbb;
|
|
''');
|
|
});
|
|
}
|
|
|
|
test_init_fatalError_invalidStatement() {
|
|
addTestFile('''
|
|
main(bool b) {
|
|
// start
|
|
if (b) {
|
|
print(1);
|
|
// end
|
|
print(2);
|
|
}
|
|
}
|
|
''');
|
|
_setOffsetLengthForStartEnd();
|
|
return waitForTasksFinished().then((_) {
|
|
return _sendExtractRequest();
|
|
}).then((Response response) {
|
|
var result = new EditGetRefactoringResult.fromResponse(response);
|
|
assertResultProblemsFatal(result.initialProblems);
|
|
// ...there is no any feedback
|
|
expect(result.feedback, isNull);
|
|
});
|
|
}
|
|
|
|
test_long_expression() {
|
|
addTestFile('''
|
|
main() {
|
|
print(1 +
|
|
2);
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('1 +\n 2');
|
|
return assertSuccessfulRefactoring(_computeChange, '''
|
|
main() {
|
|
print(res());
|
|
}
|
|
|
|
int res() {
|
|
return 1 +
|
|
2;
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_names() {
|
|
addTestFile('''
|
|
class TreeItem {}
|
|
TreeItem getSelectedItem() => null;
|
|
main() {
|
|
var a = getSelectedItem( );
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('getSelectedItem( )');
|
|
return _computeInitialFeedback().then((feedback) {
|
|
expect(feedback.names,
|
|
unorderedEquals(['treeItem', 'item', 'selectedItem']));
|
|
expect(feedback.returnType, 'TreeItem');
|
|
});
|
|
}
|
|
|
|
test_offsetsLengths() {
|
|
addTestFile('''
|
|
class TreeItem {}
|
|
TreeItem getSelectedItem() => null;
|
|
main() {
|
|
var a = 1 + 2;
|
|
var b = 1 + 2;
|
|
}
|
|
''');
|
|
_setOffsetLengthForString('1 + 2');
|
|
return _computeInitialFeedback().then((feedback) {
|
|
expect(feedback.offsets, [findOffset('1 + 2'), findOffset('1 + 2')]);
|
|
expect(feedback.lengths, [5, 6]);
|
|
});
|
|
}
|
|
|
|
test_statements() {
|
|
addTestFile('''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
// start
|
|
print(a + b);
|
|
// end
|
|
print(a + b);
|
|
}
|
|
''');
|
|
_setOffsetLengthForStartEnd();
|
|
return assertSuccessfulRefactoring(_computeChange, '''
|
|
main() {
|
|
int a = 1;
|
|
int b = 2;
|
|
// start
|
|
res(a, b);
|
|
// end
|
|
res(a, b);
|
|
}
|
|
|
|
void res(int a, int b) {
|
|
print(a + b);
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<Response> _computeChange() async {
|
|
await _prepareOptions();
|
|
// send request with the options
|
|
return _sendExtractRequest();
|
|
}
|
|
|
|
Future<ExtractMethodFeedback> _computeInitialFeedback() async {
|
|
await waitForTasksFinished();
|
|
Response response = await _sendExtractRequest();
|
|
var result = new EditGetRefactoringResult.fromResponse(response);
|
|
return result.feedback;
|
|
}
|
|
|
|
Future _prepareOptions() {
|
|
return getRefactoringResult(() {
|
|
// get initial feedback
|
|
return _sendExtractRequest();
|
|
}).then((result) {
|
|
assertResultProblemsOK(result);
|
|
// fill options from result
|
|
ExtractMethodFeedback feedback = result.feedback;
|
|
options = new ExtractMethodOptions(
|
|
feedback.returnType, false, name, feedback.parameters, true);
|
|
// done
|
|
return new Future.value();
|
|
});
|
|
}
|
|
|
|
Future<Response> _sendExtractRequest() {
|
|
RefactoringKind kind = RefactoringKind.EXTRACT_METHOD;
|
|
return sendRequest(kind, offset, length, options, false);
|
|
}
|
|
|
|
void _setOffsetLengthForStartEnd() {
|
|
offset = findOffset('// start') + '// start\n'.length;
|
|
length = findOffset('// end') - offset;
|
|
}
|
|
|
|
void _setOffsetLengthForString(String search) {
|
|
offset = findOffset(search);
|
|
length = search.length;
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class GetAvailableRefactoringsTest extends AbstractAnalysisTest {
|
|
List<RefactoringKind> kinds;
|
|
|
|
void addFlutterPackage() {
|
|
var libFolder = flutter.configureFlutterPackage(resourceProvider);
|
|
// Create .packages in the project.
|
|
newFile(join(projectPath, '.packages'), content: '''
|
|
flutter:${libFolder.toUri()}
|
|
''');
|
|
}
|
|
|
|
/**
|
|
* Tests that there is refactoring of the given [kind] is available at the
|
|
* [search] offset.
|
|
*/
|
|
Future assertHasKind(
|
|
String code, String search, RefactoringKind kind, bool expected) async {
|
|
addTestFile(code);
|
|
await waitForTasksFinished();
|
|
await getRefactoringsAtString(search);
|
|
// verify
|
|
Matcher matcher = contains(kind);
|
|
if (!expected) {
|
|
matcher = isNot(matcher);
|
|
}
|
|
expect(kinds, matcher);
|
|
}
|
|
|
|
/**
|
|
* Tests that there is a RENAME refactoring available at the [search] offset.
|
|
*/
|
|
Future assertHasRenameRefactoring(String code, String search) async {
|
|
return assertHasKind(code, search, RefactoringKind.RENAME, true);
|
|
}
|
|
|
|
/**
|
|
* Returns the list of available refactorings for the given [offset] and
|
|
* [length].
|
|
*/
|
|
Future getRefactorings(int offset, int length) async {
|
|
Request request =
|
|
new EditGetAvailableRefactoringsParams(testFile, offset, length)
|
|
.toRequest('0');
|
|
serverChannel.sendRequest(request);
|
|
var response = await serverChannel.waitForResponse(request);
|
|
var result = new EditGetAvailableRefactoringsResult.fromResponse(response);
|
|
kinds = result.kinds;
|
|
}
|
|
|
|
/**
|
|
* Returns the list of available refactorings at the offset of [search].
|
|
*/
|
|
Future getRefactoringsAtString(String search) {
|
|
int offset = findOffset(search);
|
|
return getRefactorings(offset, 0);
|
|
}
|
|
|
|
Future getRefactoringsForString(String search) {
|
|
int offset = findOffset(search);
|
|
return getRefactorings(offset, search.length);
|
|
}
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
createProject();
|
|
handler = new EditDomainHandler(server);
|
|
server.handlers = [handler];
|
|
}
|
|
|
|
Future test_convertMethodToGetter_hasElement() {
|
|
return assertHasKind('''
|
|
int getValue() => 42;
|
|
''', 'getValue', RefactoringKind.CONVERT_METHOD_TO_GETTER, true);
|
|
}
|
|
|
|
Future test_extractLocal() async {
|
|
addTestFile('''
|
|
main() {
|
|
var a = 1 + 2;
|
|
}
|
|
''');
|
|
await waitForTasksFinished();
|
|
await getRefactoringsForString('1 + 2');
|
|
expect(kinds, contains(RefactoringKind.EXTRACT_LOCAL_VARIABLE));
|
|
expect(kinds, contains(RefactoringKind.EXTRACT_METHOD));
|
|
}
|
|
|
|
Future test_extractLocal_withoutSelection() async {
|
|
addTestFile('''
|
|
main() {
|
|
var a = 1 + 2;
|
|
}
|
|
''');
|
|
await waitForTasksFinished();
|
|
await getRefactoringsAtString('1 + 2');
|
|
expect(kinds, contains(RefactoringKind.EXTRACT_LOCAL_VARIABLE));
|
|
expect(kinds, contains(RefactoringKind.EXTRACT_METHOD));
|
|
}
|
|
|
|
Future test_extractWidget() async {
|
|
addFlutterPackage();
|
|
addTestFile('''
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return new Text('AAA');
|
|
}
|
|
}
|
|
''');
|
|
await waitForTasksFinished();
|
|
await getRefactoringsForString('new Text');
|
|
expect(kinds, contains(RefactoringKind.EXTRACT_WIDGET));
|
|
}
|
|
|
|
test_invalidFilePathFormat_notAbsolute() async {
|
|
var request = new EditGetAvailableRefactoringsParams('test.dart', 0, 0)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
test_invalidFilePathFormat_notNormalized() async {
|
|
var request = new EditGetAvailableRefactoringsParams(
|
|
convertPath('/foo/../bar/test.dart'), 0, 0)
|
|
.toRequest('0');
|
|
var response = await waitResponse(request);
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future test_rename_hasElement_class() {
|
|
return assertHasRenameRefactoring('''
|
|
class Test {}
|
|
main() {
|
|
Test v;
|
|
}
|
|
''', 'Test v');
|
|
}
|
|
|
|
Future test_rename_hasElement_constructor() {
|
|
return assertHasRenameRefactoring('''
|
|
class A {
|
|
A.test() {}
|
|
}
|
|
main() {
|
|
new A.test();
|
|
}
|
|
''', 'test();');
|
|
}
|
|
|
|
Future test_rename_hasElement_function() {
|
|
return assertHasRenameRefactoring('''
|
|
main() {
|
|
test();
|
|
}
|
|
test() {}
|
|
''', 'test();');
|
|
}
|
|
|
|
Future test_rename_hasElement_importElement_directive() {
|
|
return assertHasRenameRefactoring('''
|
|
import 'dart:math' as math;
|
|
main() {
|
|
math.PI;
|
|
}
|
|
''', 'import ');
|
|
}
|
|
|
|
Future test_rename_hasElement_importElement_prefixDecl() {
|
|
return assertHasRenameRefactoring('''
|
|
import 'dart:math' as math;
|
|
main() {
|
|
math.PI;
|
|
}
|
|
''', 'math;');
|
|
}
|
|
|
|
Future test_rename_hasElement_importElement_prefixRef() {
|
|
return assertHasRenameRefactoring('''
|
|
import 'dart:async' as test;
|
|
import 'dart:math' as test;
|
|
main() {
|
|
test.PI;
|
|
}
|
|
''', 'test.PI;');
|
|
}
|
|
|
|
Future test_rename_hasElement_instanceGetter() {
|
|
return assertHasRenameRefactoring('''
|
|
class A {
|
|
get test => 0;
|
|
}
|
|
main(A a) {
|
|
a.test;
|
|
}
|
|
''', 'test;');
|
|
}
|
|
|
|
Future test_rename_hasElement_instanceSetter() {
|
|
return assertHasRenameRefactoring('''
|
|
class A {
|
|
set test(x) {}
|
|
}
|
|
main(A a) {
|
|
a.test = 2;
|
|
}
|
|
''', 'test = 2;');
|
|
}
|
|
|
|
Future test_rename_hasElement_library() {
|
|
return assertHasRenameRefactoring('''
|
|
library my.lib;
|
|
''', 'library ');
|
|
}
|
|
|
|
Future test_rename_hasElement_localVariable() {
|
|
return assertHasRenameRefactoring('''
|
|
main() {
|
|
int test = 0;
|
|
print(test);
|
|
}
|
|
''', 'test = 0;');
|
|
}
|
|
|
|
Future test_rename_hasElement_method() {
|
|
return assertHasRenameRefactoring('''
|
|
class A {
|
|
test() {}
|
|
}
|
|
main(A a) {
|
|
a.test();
|
|
}
|
|
''', 'test();');
|
|
}
|
|
|
|
Future test_rename_noElement() async {
|
|
addTestFile('''
|
|
main() {
|
|
// not an element
|
|
}
|
|
''');
|
|
await waitForTasksFinished();
|
|
await getRefactoringsAtString('// not an element');
|
|
expect(kinds, isNot(contains(RefactoringKind.RENAME)));
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class InlineLocalTest extends _AbstractGetRefactoring_Test {
|
|
test_analysis_onlyOneFile() async {
|
|
shouldWaitForFullAnalysis = false;
|
|
String otherFile = join(testFolder, 'other.dart');
|
|
newFile(otherFile, content: r'''
|
|
foo(int p) {}
|
|
''');
|
|
addTestFile('''
|
|
import 'other.dart';
|
|
main() {
|
|
int res = 1 + 2;
|
|
foo(res);
|
|
foo(res);
|
|
}
|
|
''');
|
|
// Start refactoring.
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return _sendInlineRequest('res =');
|
|
});
|
|
// We get the refactoring feedback....
|
|
InlineLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.occurrences, 2);
|
|
}
|
|
|
|
test_feedback() {
|
|
addTestFile('''
|
|
main() {
|
|
int test = 42;
|
|
print(test);
|
|
print(test);
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return _sendInlineRequest('test =');
|
|
}).then((result) {
|
|
InlineLocalVariableFeedback feedback = result.feedback;
|
|
expect(feedback.name, 'test');
|
|
expect(feedback.occurrences, 2);
|
|
});
|
|
}
|
|
|
|
test_init_fatalError_notVariable() {
|
|
addTestFile('main() {}');
|
|
return getRefactoringResult(() {
|
|
return _sendInlineRequest('main() {}');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(result.initialProblems,
|
|
'Local variable declaration or reference must be selected to activate this refactoring.');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_OK() {
|
|
addTestFile('''
|
|
main() {
|
|
int test = 42;
|
|
int a = test + 2;
|
|
print(test);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendInlineRequest('test + 2');
|
|
}, '''
|
|
main() {
|
|
int a = 42 + 2;
|
|
print(42);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_resetOnAnalysisSetChanged() async {
|
|
newFile(join(testFolder, 'other.dart'), content: '// other 1');
|
|
addTestFile('''
|
|
main() {
|
|
int res = 1 + 2;
|
|
print(res);
|
|
}
|
|
''');
|
|
// Send the first request.
|
|
await getRefactoringResult(() {
|
|
return _sendInlineRequest('res = ');
|
|
});
|
|
int initialResetCount = test_resetCount;
|
|
// Update the test.dart file.
|
|
modifyTestFile('''
|
|
main() {
|
|
print(1 + 2);
|
|
}
|
|
''');
|
|
// The refactoring was reset.
|
|
await pumpEventQueue();
|
|
expect(test_resetCount, initialResetCount + 1);
|
|
}
|
|
|
|
Future<Response> _sendInlineRequest(String search) {
|
|
Request request = new EditGetRefactoringParams(
|
|
RefactoringKind.INLINE_LOCAL_VARIABLE,
|
|
testFile,
|
|
findOffset(search),
|
|
0,
|
|
false)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class InlineMethodTest extends _AbstractGetRefactoring_Test {
|
|
InlineMethodOptions options = new InlineMethodOptions(true, true);
|
|
|
|
test_feedback() {
|
|
addTestFile('''
|
|
class A {
|
|
int f;
|
|
test(int p) {
|
|
print(f + p);
|
|
}
|
|
main() {
|
|
test(1);
|
|
}
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return _sendInlineRequest('test(int p)');
|
|
}).then((result) {
|
|
InlineMethodFeedback feedback = result.feedback;
|
|
expect(feedback.className, 'A');
|
|
expect(feedback.methodName, 'test');
|
|
expect(feedback.isDeclaration, isTrue);
|
|
});
|
|
}
|
|
|
|
test_init_fatalError_noMethod() {
|
|
addTestFile('// nothing to inline');
|
|
return getRefactoringResult(() {
|
|
return _sendInlineRequest('// nothing');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(result.initialProblems,
|
|
'Method declaration or reference must be selected to activate this refactoring.');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_method() {
|
|
addTestFile('''
|
|
class A {
|
|
int f;
|
|
test(int p) {
|
|
print(f + p);
|
|
}
|
|
main() {
|
|
test(1);
|
|
}
|
|
}
|
|
main(A a) {
|
|
a.test(2);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendInlineRequest('test(int p)');
|
|
}, '''
|
|
class A {
|
|
int f;
|
|
main() {
|
|
print(f + 1);
|
|
}
|
|
}
|
|
main(A a) {
|
|
print(a.f + 2);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_topLevelFunction() {
|
|
addTestFile('''
|
|
test(a, b) {
|
|
print(a + b);
|
|
}
|
|
main() {
|
|
test(1, 2);
|
|
test(10, 20);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendInlineRequest('test(a');
|
|
}, '''
|
|
main() {
|
|
print(1 + 2);
|
|
print(10 + 20);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_topLevelFunction_oneInvocation() {
|
|
addTestFile('''
|
|
test(a, b) {
|
|
print(a + b);
|
|
}
|
|
main() {
|
|
test(1, 2);
|
|
test(10, 20);
|
|
}
|
|
''');
|
|
options.deleteSource = false;
|
|
options.inlineAll = false;
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendInlineRequest('test(10,');
|
|
}, '''
|
|
test(a, b) {
|
|
print(a + b);
|
|
}
|
|
main() {
|
|
test(1, 2);
|
|
print(10 + 20);
|
|
}
|
|
''');
|
|
}
|
|
|
|
Future<Response> _sendInlineRequest(String search) {
|
|
Request request = new EditGetRefactoringParams(
|
|
RefactoringKind.INLINE_METHOD,
|
|
testFile,
|
|
findOffset(search),
|
|
0,
|
|
false,
|
|
options: options)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class MoveFileTest extends _AbstractGetRefactoring_Test {
|
|
MoveFileOptions options;
|
|
|
|
@failingTest
|
|
test_OK() {
|
|
_fail('The move file refactoring is not supported under the new driver');
|
|
newFile('/project/bin/lib.dart');
|
|
addTestFile('''
|
|
import 'dart:math';
|
|
import 'lib.dart';
|
|
''');
|
|
_setOptions('/project/test.dart');
|
|
return assertSuccessfulRefactoring(() {
|
|
return _sendMoveRequest();
|
|
}, '''
|
|
import 'dart:math';
|
|
import 'bin/lib.dart';
|
|
''');
|
|
}
|
|
|
|
Future<Response> _sendMoveRequest() {
|
|
Request request = new EditGetRefactoringParams(
|
|
RefactoringKind.MOVE_FILE, testFile, 0, 0, false,
|
|
options: options)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
|
|
void _setOptions(String newFile) {
|
|
options = new MoveFileOptions(newFile);
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class RenameTest extends _AbstractGetRefactoring_Test {
|
|
Future<Response> sendRenameRequest(String search, String newName,
|
|
{String id: '0', bool validateOnly: false}) {
|
|
RenameOptions options = newName != null ? new RenameOptions(newName) : null;
|
|
Request request = new EditGetRefactoringParams(RefactoringKind.RENAME,
|
|
testFile, findOffset(search), 0, validateOnly,
|
|
options: options)
|
|
.toRequest(id);
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
|
|
void tearDown() {
|
|
test_simulateRefactoringReset_afterInitialConditions = false;
|
|
test_simulateRefactoringReset_afterFinalConditions = false;
|
|
test_simulateRefactoringReset_afterCreateChange = false;
|
|
super.tearDown();
|
|
}
|
|
|
|
test_cancelPendingRequest() async {
|
|
addTestFile('''
|
|
main() {
|
|
int test = 0;
|
|
print(test);
|
|
}
|
|
''');
|
|
// send the "1" request, but don't wait for it
|
|
Future<Response> futureA = sendRenameRequest('test =', 'nameA', id: '1');
|
|
// send the "2" request and wait for it
|
|
Response responseB = await sendRenameRequest('test =', 'nameB', id: '2');
|
|
// wait for the (delayed) "1" response
|
|
Response responseA = await futureA;
|
|
// "1" was cancelled
|
|
// "2" is successful
|
|
expect(responseA,
|
|
isResponseFailure('1', RequestErrorCode.REFACTORING_REQUEST_CANCELLED));
|
|
expect(responseB, isResponseSuccess('2'));
|
|
}
|
|
|
|
test_class() {
|
|
addTestFile('''
|
|
class Test {
|
|
Test() {}
|
|
Test.named() {}
|
|
}
|
|
main() {
|
|
Test v;
|
|
new Test();
|
|
new Test.named();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('Test {', 'NewName');
|
|
}, '''
|
|
class NewName {
|
|
NewName() {}
|
|
NewName.named() {}
|
|
}
|
|
main() {
|
|
NewName v;
|
|
new NewName();
|
|
new NewName.named();
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_class_fromFactoryRedirectingConstructor() {
|
|
addTestFile('''
|
|
class A {
|
|
A() = Test.named;
|
|
}
|
|
class Test {
|
|
Test.named() {}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('Test.named;', 'NewName');
|
|
},
|
|
'''
|
|
class A {
|
|
A() = NewName.named;
|
|
}
|
|
class NewName {
|
|
NewName.named() {}
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 18);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_class_fromInstanceCreation() {
|
|
addTestFile('''
|
|
class Test {
|
|
Test() {}
|
|
}
|
|
main() {
|
|
new Test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('Test();', 'NewName');
|
|
},
|
|
'''
|
|
class NewName {
|
|
NewName() {}
|
|
}
|
|
main() {
|
|
new NewName();
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 42);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_class_fromInstanceCreation_namedConstructor() {
|
|
addTestFile('''
|
|
class Test {
|
|
Test.named() {}
|
|
}
|
|
main() {
|
|
new Test.named();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('Test.named();', 'NewName');
|
|
},
|
|
'''
|
|
class NewName {
|
|
NewName.named() {}
|
|
}
|
|
main() {
|
|
new NewName.named();
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 48);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_class_fromInstanceCreation_onNew() {
|
|
addTestFile('''
|
|
class Test {
|
|
Test() {}
|
|
}
|
|
main() {
|
|
new Test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('new Test();', 'NewName');
|
|
},
|
|
'''
|
|
class NewName {
|
|
NewName() {}
|
|
}
|
|
main() {
|
|
new NewName();
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 42);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_class_fromInstanceCreation_onNew_namedConstructor() {
|
|
addTestFile('''
|
|
class Test {
|
|
Test.named() {}
|
|
}
|
|
main() {
|
|
new Test.named();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('new Test.named();', 'NewName');
|
|
},
|
|
'''
|
|
class NewName {
|
|
NewName.named() {}
|
|
}
|
|
main() {
|
|
new NewName.named();
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 48);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_class_options_fatalError() {
|
|
addTestFile('''
|
|
class Test {}
|
|
main() {
|
|
Test v;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('Test {}', '');
|
|
}).then((result) {
|
|
assertResultProblemsFatal(
|
|
result.optionsProblems, 'Class name must not be empty.');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_class_validateOnly() {
|
|
addTestFile('''
|
|
class Test {}
|
|
main() {
|
|
Test v;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('Test {}', 'NewName', validateOnly: true);
|
|
}).then((result) {
|
|
RenameFeedback feedback = result.feedback;
|
|
assertResultProblemsOK(result);
|
|
expect(feedback.elementKindName, 'class');
|
|
expect(feedback.oldName, 'Test');
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_class_warning() {
|
|
addTestFile('''
|
|
class Test {}
|
|
main() {
|
|
Test v;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('Test {}', 'newName');
|
|
}).then((result) {
|
|
assertResultProblemsWarning(result.optionsProblems,
|
|
'Class name should start with an uppercase letter.');
|
|
// ...but there is still a change
|
|
assertTestRefactoringResult(result, '''
|
|
class newName {}
|
|
main() {
|
|
newName v;
|
|
}
|
|
''');
|
|
}).then((_) {
|
|
// "NewName" is a perfectly valid name
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('Test {}', 'NewName');
|
|
}).then((result) {
|
|
assertResultProblemsOK(result);
|
|
// ...and there is a new change
|
|
assertTestRefactoringResult(result, '''
|
|
class NewName {}
|
|
main() {
|
|
NewName v;
|
|
}
|
|
''');
|
|
});
|
|
});
|
|
}
|
|
|
|
test_classMember_field() {
|
|
addTestFile('''
|
|
class A {
|
|
var test = 0;
|
|
A(this.test);
|
|
main() {
|
|
print(test);
|
|
}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test = 0', 'newName');
|
|
}, '''
|
|
class A {
|
|
var newName = 0;
|
|
A(this.newName);
|
|
main() {
|
|
print(newName);
|
|
}
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_classMember_field_onFieldFormalParameter() {
|
|
addTestFile('''
|
|
class A {
|
|
var test = 0;
|
|
A(this.test);
|
|
main() {
|
|
print(test);
|
|
}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test);', 'newName');
|
|
}, '''
|
|
class A {
|
|
var newName = 0;
|
|
A(this.newName);
|
|
main() {
|
|
print(newName);
|
|
}
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_classMember_field_onFieldFormalParameter_named() {
|
|
addTestFile('''
|
|
class A {
|
|
final int test;
|
|
A({this.test: 0});
|
|
}
|
|
main() {
|
|
new A(test: 42);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test: 42', 'newName');
|
|
}, '''
|
|
class A {
|
|
final int newName;
|
|
A({this.newName: 0});
|
|
}
|
|
main() {
|
|
new A(newName: 42);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_classMember_getter() {
|
|
addTestFile('''
|
|
class A {
|
|
get test => 0;
|
|
main() {
|
|
print(test);
|
|
}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test =>', 'newName');
|
|
}, '''
|
|
class A {
|
|
get newName => 0;
|
|
main() {
|
|
print(newName);
|
|
}
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_classMember_method() {
|
|
addTestFile('''
|
|
class A {
|
|
test() {}
|
|
main() {
|
|
test();
|
|
}
|
|
}
|
|
main(A a) {
|
|
a.test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test() {}', 'newName');
|
|
}, '''
|
|
class A {
|
|
newName() {}
|
|
main() {
|
|
newName();
|
|
}
|
|
}
|
|
main(A a) {
|
|
a.newName();
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_classMember_method_potential() {
|
|
addTestFile('''
|
|
class A {
|
|
test() {}
|
|
}
|
|
main(A a, a2) {
|
|
a.test();
|
|
a2.test(); // a2
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('test() {}', 'newName');
|
|
}).then((result) {
|
|
assertResultProblemsOK(result);
|
|
// prepare potential edit ID
|
|
List<String> potentialIds = result.potentialEdits;
|
|
expect(potentialIds, hasLength(1));
|
|
String potentialId = potentialIds[0];
|
|
// find potential edit
|
|
SourceChange change = result.change;
|
|
SourceEdit potentialEdit = _findEditWithId(change, potentialId);
|
|
expect(potentialEdit, isNotNull);
|
|
expect(potentialEdit.offset, findOffset('test(); // a2'));
|
|
expect(potentialEdit.length, 4);
|
|
});
|
|
}
|
|
|
|
test_classMember_setter() {
|
|
addTestFile('''
|
|
class A {
|
|
set test(x) {}
|
|
main() {
|
|
test = 0;
|
|
}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test = 0', 'newName');
|
|
}, '''
|
|
class A {
|
|
set newName(x) {}
|
|
main() {
|
|
newName = 0;
|
|
}
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_constructor_fromFactoryRedirectingConstructor() {
|
|
addTestFile('''
|
|
class A {
|
|
A() = B.test;
|
|
}
|
|
class B {
|
|
B.test() {}
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('test;', 'newName');
|
|
},
|
|
'''
|
|
class A {
|
|
A() = B.newName;
|
|
}
|
|
class B {
|
|
B.newName() {}
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 20);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_constructor_fromInstanceCreation() {
|
|
addTestFile('''
|
|
class A {
|
|
A.test() {}
|
|
}
|
|
main() {
|
|
new A.test();
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest('test();', 'newName');
|
|
},
|
|
'''
|
|
class A {
|
|
A.newName() {}
|
|
}
|
|
main() {
|
|
new A.newName();
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 43);
|
|
expect(renameFeedback.length, 4);
|
|
},
|
|
);
|
|
}
|
|
|
|
test_feedback() {
|
|
addTestFile('''
|
|
class Test {}
|
|
main() {
|
|
Test v;
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('st v;', 'NewName');
|
|
}).then((result) {
|
|
RenameFeedback feedback = result.feedback;
|
|
expect(feedback, isNotNull);
|
|
expect(feedback.offset, findOffset('Test v;'));
|
|
expect(feedback.length, 'Test'.length);
|
|
});
|
|
}
|
|
|
|
test_function() {
|
|
addTestFile('''
|
|
test() {}
|
|
main() {
|
|
test();
|
|
print(test);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test() {}', 'newName');
|
|
}, '''
|
|
newName() {}
|
|
main() {
|
|
newName();
|
|
print(newName);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_importPrefix_add() {
|
|
addTestFile('''
|
|
import 'dart:math';
|
|
import 'dart:async';
|
|
main() {
|
|
Random r;
|
|
Future f;
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest("import 'dart:async';", 'new_name');
|
|
},
|
|
'''
|
|
import 'dart:math';
|
|
import 'dart:async' as new_name;
|
|
main() {
|
|
Random r;
|
|
new_name.Future f;
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, -1);
|
|
expect(renameFeedback.length, 0);
|
|
});
|
|
}
|
|
|
|
test_importPrefix_remove() {
|
|
addTestFile('''
|
|
import 'dart:math' as test;
|
|
import 'dart:async' as test;
|
|
main() {
|
|
test.Random r;
|
|
test.Future f;
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(
|
|
() {
|
|
return sendRenameRequest("import 'dart:async' as test;", '');
|
|
},
|
|
'''
|
|
import 'dart:math' as test;
|
|
import 'dart:async';
|
|
main() {
|
|
test.Random r;
|
|
Future f;
|
|
}
|
|
''',
|
|
feedbackValidator: (feedback) {
|
|
RenameFeedback renameFeedback = feedback;
|
|
expect(renameFeedback.offset, 51);
|
|
expect(renameFeedback.length, 4);
|
|
});
|
|
}
|
|
|
|
test_init_fatalError_noElement() {
|
|
addTestFile('// nothing to rename');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('// nothing', null);
|
|
}).then((result) {
|
|
assertResultProblemsFatal(
|
|
result.initialProblems, 'Unable to create a refactoring');
|
|
// ...there is no any change
|
|
expect(result.change, isNull);
|
|
});
|
|
}
|
|
|
|
test_library_libraryDirective() {
|
|
addTestFile('''
|
|
library aaa.bbb.ccc;
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('library aaa', 'my.new_name');
|
|
}, '''
|
|
library my.new_name;
|
|
''');
|
|
}
|
|
|
|
test_library_libraryDirective_name() {
|
|
addTestFile('''
|
|
library aaa.bbb.ccc;
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('aaa', 'my.new_name');
|
|
}, '''
|
|
library my.new_name;
|
|
''');
|
|
}
|
|
|
|
test_library_libraryDirective_nameDot() {
|
|
addTestFile('''
|
|
library aaa.bbb.ccc;
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('.bbb', 'my.new_name');
|
|
}, '''
|
|
library my.new_name;
|
|
''');
|
|
}
|
|
|
|
test_library_partOfDirective() {
|
|
newFile(join(testFolder, 'my_lib.dart'), content: '''
|
|
library aaa.bbb.ccc;
|
|
part 'test.dart';
|
|
''');
|
|
addTestFile('''
|
|
part of aaa.bbb.ccc;
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('aaa.bb', 'my.new_name');
|
|
}, '''
|
|
part of my.new_name;
|
|
''');
|
|
}
|
|
|
|
test_localVariable() {
|
|
addTestFile('''
|
|
main() {
|
|
int test = 0;
|
|
test = 1;
|
|
test += 2;
|
|
print(test);
|
|
}
|
|
''');
|
|
return assertSuccessfulRefactoring(() {
|
|
return sendRenameRequest('test = 1', 'newName');
|
|
}, '''
|
|
main() {
|
|
int newName = 0;
|
|
newName = 1;
|
|
newName += 2;
|
|
print(newName);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test_localVariable_finalCheck_shadowError() {
|
|
addTestFile('''
|
|
main() {
|
|
var newName;
|
|
int test = 0;
|
|
print(test);
|
|
}
|
|
''');
|
|
return getRefactoringResult(() {
|
|
return sendRenameRequest('test = 0', 'newName');
|
|
}).then((result) {
|
|
List<RefactoringProblem> problems = result.finalProblems;
|
|
expect(problems, hasLength(1));
|
|
assertResultProblemsError(
|
|
problems, "Duplicate local variable 'newName'.");
|
|
});
|
|
}
|
|
|
|
test_reset_afterCreateChange() {
|
|
test_simulateRefactoringReset_afterCreateChange = true;
|
|
addTestFile('''
|
|
test() {}
|
|
main() {
|
|
test();
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendRenameRequest('test() {}', 'newName').then((response) {
|
|
_expectRefactoringRequestCancelled(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
test_reset_afterFinalConditions() {
|
|
test_simulateRefactoringReset_afterFinalConditions = true;
|
|
addTestFile('''
|
|
test() {}
|
|
main() {
|
|
test();
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendRenameRequest('test() {}', 'newName').then((response) {
|
|
_expectRefactoringRequestCancelled(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
test_reset_afterInitialConditions() {
|
|
test_simulateRefactoringReset_afterInitialConditions = true;
|
|
addTestFile('''
|
|
test() {}
|
|
main() {
|
|
test();
|
|
}
|
|
''');
|
|
return waitForTasksFinished().then((_) {
|
|
return sendRenameRequest('test() {}', 'newName').then((response) {
|
|
_expectRefactoringRequestCancelled(response);
|
|
});
|
|
});
|
|
}
|
|
|
|
test_resetOnAnalysis() async {
|
|
addTestFile('''
|
|
main() {
|
|
int initialName = 0;
|
|
print(initialName);
|
|
}
|
|
''');
|
|
// send the first request
|
|
EditGetRefactoringResult result = await getRefactoringResult(() {
|
|
return sendRenameRequest('initialName =', 'newName', validateOnly: true);
|
|
});
|
|
_validateFeedback(result, oldName: 'initialName');
|
|
// update the file
|
|
modifyTestFile('''
|
|
main() {
|
|
int otherName = 0;
|
|
print(otherName);
|
|
}
|
|
''');
|
|
server.getAnalysisDriver(testFile).getResult(testFile);
|
|
// send the second request, with the same kind, file and offset
|
|
await waitForTasksFinished();
|
|
result = await getRefactoringResult(() {
|
|
return sendRenameRequest('otherName =', 'newName', validateOnly: true);
|
|
});
|
|
// the refactoring was reset, so we don't get a stale result
|
|
_validateFeedback(result, oldName: 'otherName');
|
|
}
|
|
|
|
void _expectRefactoringRequestCancelled(Response response) {
|
|
expect(response.error, isNotNull);
|
|
expect(response,
|
|
isResponseFailure('0', RequestErrorCode.REFACTORING_REQUEST_CANCELLED));
|
|
}
|
|
|
|
SourceEdit _findEditWithId(SourceChange change, String id) {
|
|
SourceEdit potentialEdit;
|
|
change.edits.forEach((fileEdit) {
|
|
fileEdit.edits.forEach((edit) {
|
|
if (edit.id == id) {
|
|
potentialEdit = edit;
|
|
}
|
|
});
|
|
});
|
|
return potentialEdit;
|
|
}
|
|
|
|
void _validateFeedback(EditGetRefactoringResult result, {String oldName}) {
|
|
RenameFeedback feedback = result.feedback;
|
|
expect(feedback, isNotNull);
|
|
if (oldName != null) {
|
|
expect(feedback.oldName, oldName);
|
|
}
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class _AbstractGetRefactoring_Test extends AbstractAnalysisTest {
|
|
bool shouldWaitForFullAnalysis = true;
|
|
|
|
/**
|
|
* Asserts that [problems] has a single ERROR problem.
|
|
*/
|
|
void assertResultProblemsError(List<RefactoringProblem> problems,
|
|
[String message]) {
|
|
RefactoringProblem problem = problems[0];
|
|
expect(problem.severity, RefactoringProblemSeverity.ERROR,
|
|
reason: problem.toString());
|
|
if (message != null) {
|
|
expect(problem.message, message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that [result] has a single FATAL problem.
|
|
*/
|
|
void assertResultProblemsFatal(List<RefactoringProblem> problems,
|
|
[String message]) {
|
|
RefactoringProblem problem = problems[0];
|
|
expect(problems, hasLength(1));
|
|
expect(problem.severity, RefactoringProblemSeverity.FATAL,
|
|
reason: problem.toString());
|
|
if (message != null) {
|
|
expect(problem.message, message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts that [result] has no problems at all.
|
|
*/
|
|
void assertResultProblemsOK(EditGetRefactoringResult result) {
|
|
expect(result.initialProblems, isEmpty);
|
|
expect(result.optionsProblems, isEmpty);
|
|
expect(result.finalProblems, isEmpty);
|
|
}
|
|
|
|
/**
|
|
* Asserts that [result] has a single WARNING problem.
|
|
*/
|
|
void assertResultProblemsWarning(List<RefactoringProblem> problems,
|
|
[String message]) {
|
|
RefactoringProblem problem = problems[0];
|
|
expect(problems, hasLength(1));
|
|
expect(problem.severity, RefactoringProblemSeverity.WARNING,
|
|
reason: problem.toString());
|
|
if (message != null) {
|
|
expect(problem.message, message);
|
|
}
|
|
}
|
|
|
|
Future assertSuccessfulRefactoring(
|
|
Future<Response> requestSender(), String expectedCode,
|
|
{void Function(RefactoringFeedback) feedbackValidator}) async {
|
|
EditGetRefactoringResult result = await getRefactoringResult(requestSender);
|
|
assertResultProblemsOK(result);
|
|
if (feedbackValidator != null) {
|
|
feedbackValidator(result.feedback);
|
|
}
|
|
assertTestRefactoringResult(result, expectedCode);
|
|
}
|
|
|
|
/**
|
|
* Asserts that the given [EditGetRefactoringResult] has a [testFile] change
|
|
* which results in the [expectedCode].
|
|
*/
|
|
void assertTestRefactoringResult(
|
|
EditGetRefactoringResult result, String expectedCode) {
|
|
SourceChange change = result.change;
|
|
expect(change, isNotNull);
|
|
for (SourceFileEdit fileEdit in change.edits) {
|
|
if (fileEdit.file == testFile) {
|
|
String actualCode = SourceEdit.applySequence(testCode, fileEdit.edits);
|
|
expect(actualCode, expectedCode);
|
|
return;
|
|
}
|
|
}
|
|
fail('No SourceFileEdit for $testFile in $change');
|
|
}
|
|
|
|
Future<EditGetRefactoringResult> getRefactoringResult(
|
|
Future<Response> requestSender()) async {
|
|
if (shouldWaitForFullAnalysis) {
|
|
await waitForTasksFinished();
|
|
}
|
|
Response response = await requestSender();
|
|
return new EditGetRefactoringResult.fromResponse(response);
|
|
}
|
|
|
|
Future<Response> sendRequest(
|
|
RefactoringKind kind, int offset, int length, RefactoringOptions options,
|
|
[bool validateOnly = false]) {
|
|
Request request = new EditGetRefactoringParams(
|
|
kind, testFile, offset, length, validateOnly,
|
|
options: options)
|
|
.toRequest('0');
|
|
return serverChannel.sendRequest(request);
|
|
}
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
createProject();
|
|
handler = new EditDomainHandler(server);
|
|
server.handlers = [handler];
|
|
}
|
|
}
|