| // Copyright (c) 2020, 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 'package:analysis_server/src/cider/fixes.dart'; | |
| import 'package:analysis_server/src/services/correction/fix.dart'; | |
| import 'package:analysis_server_plugin/edit/fix/fix.dart'; | |
| import 'package:analyzer/source/line_info.dart'; | |
| import 'package:analyzer/src/test_utilities/test_code_format.dart'; | |
| import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../../utils/matchers.dart'; | |
| import 'cider_service.dart'; | |
| void main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(CiderFixesComputerTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class CiderFixesComputerTest extends CiderServiceTest { | |
| late _CorrectionContext _correctionContext; | |
| late List<CiderErrorFixes> _errorsFixes; | |
| void assertHasFix(FixKind kind, String expected) { | |
| var fix = _getFix(kind); | |
| var fileEdits = fix.change.edits; | |
| expect(fileEdits, hasLength(1)); | |
| var resultContent = SourceEdit.applySequence( | |
| _correctionContext.content, | |
| fileEdits.single.edits, | |
| ); | |
| expect(resultContent, equalsNormalized(expected)); | |
| } | |
| Future<void> test_cachedResolvedFiles() async { | |
| await _compute(r''' | |
| var a = 0^ var b = 1 | |
| '''); | |
| // Only the first fix is applied. | |
| assertHasFix(DartFixKind.insertSemicolon, r''' | |
| var a = 0; var b = 1 | |
| '''); | |
| // The file was resolved only once, even though we have 2 errors. | |
| expect(testData.resolvedLibraries, [convertPath(testPath)]); | |
| } | |
| Future<void> test_createMethod() async { | |
| await _compute(r''' | |
| class A { | |
| } | |
| void f(A a) { | |
| a.foo(0);^ | |
| } | |
| '''); | |
| assertHasFix(DartFixKind.createMethod, r''' | |
| class A { | |
| void foo(int i) {} | |
| } | |
| void f(A a) { | |
| a.foo(0); | |
| } | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withClass() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| class Test {} | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f(Test a) {}^ | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f(Test a) {} | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withEnum() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| enum Test {a, b, c} | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f(Test a) {}^ | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f(Test a) {} | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withExtension() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| extension E on int { | |
| void foo() {} | |
| } | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f() { | |
| E(0).foo();^ | |
| } | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f() { | |
| E(0).foo(); | |
| } | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withFunction() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| void foo() {} | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f() { | |
| foo();^ | |
| } | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f() { | |
| foo(); | |
| } | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withMixin() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| mixin Test {} | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f(Test a) {}^ | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f(Test a) {} | |
| '''); | |
| } | |
| Future<void> test_importLibrary_withTopLevelVariable() async { | |
| var a = newFile('/workspace/dart/test/lib/a.dart', r''' | |
| var a = 0; | |
| '''); | |
| await fileResolver.resolve(path: a.path); | |
| await _compute(r''' | |
| void f() { | |
| a;^ | |
| } | |
| '''); | |
| assertHasFix(DartFixKind.importLibraryProject1, r''' | |
| import 'a.dart'; | |
| void f() { | |
| a; | |
| } | |
| '''); | |
| } | |
| Future<void> test_insertSemicolon() async { | |
| await _compute(r''' | |
| var v = 0^ | |
| '''); | |
| assertHasFix(DartFixKind.insertSemicolon, r''' | |
| var v = 0; | |
| '''); | |
| } | |
| Future<void> _compute(String content) async { | |
| _updateFile(content); | |
| _errorsFixes = await CiderFixesComputer( | |
| logger, | |
| fileResolver, | |
| ).compute(convertPath(testPath), _correctionContext.line); | |
| } | |
| Fix _getFix(FixKind kind) { | |
| for (var errorFixes in _errorsFixes) { | |
| for (var fix in errorFixes.fixes) { | |
| if (fix.kind == kind) { | |
| return fix; | |
| } | |
| } | |
| } | |
| fail('No fix $kind'); | |
| } | |
| void _updateFile(String content) { | |
| var code = TestCode.parseNormalized(content); | |
| content = code.code; | |
| var offset = code.position.offset; | |
| var lineInfo = LineInfo.fromContent(content); | |
| var location = lineInfo.getLocation(offset); | |
| newFile(testPath, content); | |
| _correctionContext = _CorrectionContext( | |
| content, | |
| offset, | |
| location.lineNumber, | |
| location.columnNumber, | |
| ); | |
| } | |
| } | |
| class _CorrectionContext { | |
| final String content; | |
| final int offset; | |
| final int line; | |
| final int character; | |
| new(this.content, this.offset, this.line, this.character); | |
| } |