| // Copyright (c) 2016, 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:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/file_system/memory_file_system.dart'; |
| import 'package:analyzer/src/dart/sdk/patch.dart'; |
| import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(SdkPatcherTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class SdkPatcherTest { |
| MemoryResourceProvider provider = new MemoryResourceProvider(); |
| Folder sdkFolder; |
| FolderBasedDartSdk sdk; |
| |
| SdkPatcher patcher = new SdkPatcher(); |
| RecordingErrorListener listener = new RecordingErrorListener(); |
| |
| void setUp() { |
| sdkFolder = provider.getFolder(_p('/sdk')); |
| } |
| |
| test_class_constructor_append_fail_notPrivate_named() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C {} |
| ''', r''' |
| @patch |
| class C { |
| C.named() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_append_fail_notPrivate_unnamed() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C {} |
| ''', r''' |
| @patch |
| class C { |
| C() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_append_named() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| } |
| ''', r''' |
| @patch |
| class C { |
| C._named() {} |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {C._named() {}}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| ConstructorDeclaration constructor = clazz.members[0]; |
| _assertPrevNextToken(clazz.leftBracket, constructor.beginToken); |
| _assertPrevNextToken(constructor.endToken, clazz.rightBracket); |
| } |
| |
| test_class_constructor_append_unnamed() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class _C { |
| } |
| ''', r''' |
| @patch |
| class _C { |
| _C() {} |
| } |
| '''); |
| _assertUnitCode(unit, 'class _C {_C() {}}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| ConstructorDeclaration constructor = clazz.members[0]; |
| _assertPrevNextToken(clazz.leftBracket, constructor.beginToken); |
| _assertPrevNextToken(constructor.endToken, clazz.rightBracket); |
| } |
| |
| test_class_constructor_patch() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| external C.named(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() { |
| print(42); |
| } |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {C.named() {print(42);}}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| ConstructorDeclaration constructor = clazz.members[0]; |
| expect(constructor.externalKeyword, isNull); |
| _assertPrevNextToken( |
| constructor.parameters.endToken, constructor.body.beginToken); |
| _assertPrevNextToken(constructor.endToken, clazz.rightBracket); |
| } |
| |
| test_class_constructor_patch_fail_baseFactory_patchGenerative() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external factory C.named(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_baseGenerative_patchFactory() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external C.named(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| factory C.named() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_fieldFormalParam_inBase() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| int f; |
| external C.named(this.f); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() : f = 2 {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_fieldFormalParam_inPatch() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| int f; |
| external C.named(int f); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named(this.f) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_fieldFormalParam_inPatchAndBase() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| int f; |
| external C.named(this.f); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named(this.f) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_hasInitializers() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| int f; |
| external C.named() : f = 1; |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() : f = 2 {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_noExternalKeyword() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| C.named(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_signatureChange() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external C.named(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named(double x) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_fail_signatureChange_nameOnly() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external C.named(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named(int y) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_constructor_patch_initializers() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| int f; |
| external C.named(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| C.named() : f = 2 { |
| print(42); |
| } |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {int f; C.named() : f = 2 {print(42);}}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| ConstructorDeclaration constructor = clazz.members[1]; |
| expect(constructor.externalKeyword, isNull); |
| _assertPrevNextToken(constructor.parameters.endToken, |
| constructor.initializers.beginToken.previous); |
| _assertPrevNextToken(constructor.endToken, clazz.rightBracket); |
| } |
| |
| test_class_field_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| void a() {} |
| } |
| ''', r''' |
| @patch |
| class C { |
| int _b = 42; |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {void a() {} int _b = 42;}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| MethodDeclaration a = clazz.members[0]; |
| FieldDeclaration b = clazz.members[1]; |
| _assertPrevNextToken(a.endToken, b.beginToken); |
| _assertPrevNextToken(b.endToken, clazz.rightBracket); |
| } |
| |
| test_class_field_append_fail_moreThanOne() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| @patch |
| class A { |
| @patch |
| int _f1, _f2; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_field_append_fail_notPrivate() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| @patch |
| class A { |
| @patch |
| int b; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_field_append_publicInPrivateClass() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class _C { |
| void a() {} |
| } |
| ''', r''' |
| @patch |
| class _C { |
| int b = 42; |
| } |
| '''); |
| _assertUnitCode(unit, 'class _C {void a() {} int b = 42;}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| MethodDeclaration a = clazz.members[0]; |
| FieldDeclaration b = clazz.members[1]; |
| _assertPrevNextToken(a.endToken, b.beginToken); |
| _assertPrevNextToken(b.endToken, clazz.rightBracket); |
| } |
| |
| test_class_field_patch_fail() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| @patch |
| class A { |
| @patch |
| int _f; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_getter_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| void a() {} |
| } |
| ''', r''' |
| @patch |
| class C { |
| int get _b => 2; |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {void a() {} int get _b => 2;}'); |
| } |
| |
| test_class_method_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| void a() {} |
| } |
| ''', r''' |
| @patch |
| class C { |
| void _b() {} |
| void _c() {} |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {void a() {} void _b() {} void _c() {}}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| MethodDeclaration a = clazz.members[0]; |
| MethodDeclaration b = clazz.members[1]; |
| MethodDeclaration c = clazz.members[2]; |
| _assertPrevNextToken(a.endToken, b.beginToken); |
| _assertPrevNextToken(b.endToken, c.beginToken); |
| _assertPrevNextToken(c.endToken, clazz.rightBracket); |
| } |
| |
| test_class_method_fail_notPrivate() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| @patch |
| class A { |
| void m() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| external int m(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| int m() => 42; |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {int m() => 42;}'); |
| ClassDeclaration clazz = unit.declarations[0]; |
| MethodDeclaration m = clazz.members[0]; |
| expect(m.externalKeyword, isNull); |
| _assertPrevNextToken(m.parameters.rightParenthesis, m.body.beginToken); |
| _assertPrevNextToken(m.body.endToken, clazz.rightBracket); |
| } |
| |
| test_class_method_patch_fail_noExternalKeyword() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| int m(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| int m() => 42; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(double x) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_extraArgument() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(int x) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_extraTypeTokens() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external List f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| List<int> f() => null; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_functionTypedParam_paramType() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(void x(int y)); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(void x(double y)) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_functionTypedParam_returnType() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(int x()); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(double x()) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_makeReturnTypeExplicit() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| int f() => 0; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_missingArgument() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f() {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_missingTypeTokens() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external List<int> f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| List f() => null; |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_nameOnly() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(int y) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_fail_signatureChange_returnTypeOnly() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(int x); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| int f(int x) {} |
| } |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_class_method_patch_success_defaultFormalParameter() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| external void f(int x = 0); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(int x) {} |
| } |
| '''); |
| ClassDeclaration cls = unit.declarations[0]; |
| MethodDeclaration method = cls.members[0]; |
| FormalParameter parameter = method.parameters.parameters[0]; |
| expect(parameter, new isInstanceOf<DefaultFormalParameter>()); |
| } |
| |
| test_class_method_patch_success_implicitReturnType() { |
| _doTopLevelPatching(r''' |
| class C { |
| external f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| f() => null; |
| } |
| '''); |
| } |
| |
| test_class_method_patch_success_multiTokenReturnType() { |
| _doTopLevelPatching(r''' |
| class C { |
| external List<int> f(); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| List<int> f() => null; |
| } |
| '''); |
| } |
| |
| test_class_method_patch_success_signatureChange_functionTypedParam_matching() { |
| _doTopLevelPatching(r''' |
| class C { |
| external void f(void x(int y)); |
| } |
| ''', r''' |
| @patch |
| class C { |
| @patch |
| void f(void x(int y)) {} |
| } |
| '''); |
| } |
| |
| test_class_setter_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class C { |
| void a() {} |
| } |
| ''', r''' |
| @patch |
| class C { |
| void set _b(_) {} |
| } |
| '''); |
| _assertUnitCode(unit, 'class C {void a() {} void set _b(_) {}}'); |
| } |
| |
| test_directive_fail_export() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| import 'a.dart'; |
| ''', r''' |
| export 'c.dart'; |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_directive_import() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| import 'a.dart'; |
| part 'b.dart'; |
| int bar() => 0; |
| ''', r''' |
| import 'c.dart'; |
| '''); |
| _assertUnitCode(unit, |
| "import 'a.dart'; part 'b.dart'; import 'c.dart'; int bar() => 0;"); |
| } |
| |
| test_fail_patchFileDoesNotExist() { |
| expect(() { |
| _setSdkLibraries(r''' |
| final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> { |
| 'test' : const LibraryInfo( |
| 'test/test.dart'), |
| };'''); |
| _createSdk(); |
| var patchPaths = { |
| 'dart:test': [_p('/sdk/lib/does_not_exist.dart')] |
| }; |
| File file = provider.newFile(_p('/sdk/lib/test/test.dart'), ''); |
| Source source = file.createSource(Uri.parse('dart:test')); |
| CompilationUnit unit = SdkPatcher.parse(source, true, listener); |
| patcher.patch(provider, true, patchPaths, listener, source, unit); |
| }, throwsArgumentError); |
| } |
| |
| test_internal_allowNewPublicNames() { |
| _setSdkLibraries(r''' |
| final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> { |
| '_internal' : const LibraryInfo( |
| 'internal/internal.dart'), |
| };'''); |
| var patchPaths = { |
| 'dart:_internal': [_p('/sdk/lib/internal/internal_patch.dart')] |
| }; |
| File file = provider.newFile(_p('/sdk/lib/internal/internal.dart'), r''' |
| library dart._internal; |
| class A {} |
| class B { |
| B(); |
| } |
| '''); |
| provider.newFile(_p('/sdk/lib/internal/internal_patch.dart'), r''' |
| @patch |
| class B { |
| int newField; |
| B.newConstructor(); |
| int newMethod() => 1; |
| } |
| class NewClass {} |
| int newFunction() => 2; |
| '''); |
| |
| _createSdk(); |
| |
| Source source = file.createSource(Uri.parse('dart:_internal')); |
| CompilationUnit unit = SdkPatcher.parse(source, true, listener); |
| patcher.patch(provider, true, patchPaths, listener, source, unit); |
| _assertUnitCode( |
| unit, |
| 'library dart._internal; class A {} ' |
| 'class B {B(); int newField; B.newConstructor(); int newMethod() => 1;} ' |
| 'class NewClass {} int newFunction() => 2;'); |
| } |
| |
| test_part() { |
| String baseLibCode = r''' |
| library test; |
| part 'test_part.dart'; |
| class A {} |
| '''; |
| String basePartCode = r''' |
| part of test; |
| class B {} |
| '''; |
| _setSdkLibraries(r''' |
| final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> { |
| 'test' : const LibraryInfo( |
| 'test/test.dart', |
| patches: {VM_PLATFORM: ['test/test_patch.dart']}), |
| };'''); |
| var patchPaths = { |
| 'dart:test': [_p('/sdk/lib/test/test_patch.dart')] |
| }; |
| File fileLib = provider.newFile(_p('/sdk/lib/test/test.dart'), baseLibCode); |
| File filePart = |
| provider.newFile(_p('/sdk/lib/test/test_part.dart'), basePartCode); |
| provider.newFile(_p('/sdk/lib/test/test_patch.dart'), r''' |
| import 'foo.dart'; |
| |
| @patch |
| class A { |
| int _a() => 1; |
| } |
| |
| @patch |
| class B { |
| int _b() => 1; |
| } |
| |
| class _C {} |
| '''); |
| |
| _createSdk(); |
| |
| { |
| Uri uri = Uri.parse('dart:test'); |
| Source source = fileLib.createSource(uri); |
| CompilationUnit unit = SdkPatcher.parse(source, true, listener); |
| patcher.patch(provider, true, patchPaths, listener, source, unit); |
| _assertUnitCode( |
| unit, |
| "library test; part 'test_part.dart'; import 'foo.dart'; " |
| "class A {int _a() => 1;} class _C {}"); |
| } |
| |
| { |
| Uri uri = Uri.parse('dart:test/test_part.dart'); |
| Source source = filePart.createSource(uri); |
| CompilationUnit unit = SdkPatcher.parse(source, true, listener); |
| patcher.patch(provider, true, patchPaths, listener, source, unit); |
| _assertUnitCode(unit, "part of test; class B {int _b() => 1;}"); |
| } |
| } |
| |
| test_topLevel_class_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| class _B { |
| void mmm() {} |
| } |
| '''); |
| _assertUnitCode(unit, 'class A {} class _B {void mmm() {}}'); |
| ClassDeclaration a = unit.declarations[0]; |
| ClassDeclaration b = unit.declarations[1]; |
| _assertPrevNextToken(a.endToken, b.beginToken); |
| } |
| |
| test_topLevel_class_fail_mixinApplication() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| class _B {} |
| class _C = Object with _B; |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_class_fail_notPrivate() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| class A {} |
| ''', r''' |
| class B {} |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_function_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| int foo() => 0; |
| ''', r''' |
| int _bar1() => 1; |
| int _bar2() => 2; |
| '''); |
| _assertUnitCode( |
| unit, 'int foo() => 0; int _bar1() => 1; int _bar2() => 2;'); |
| |
| FunctionDeclaration foo = unit.declarations[0]; |
| FunctionDeclaration bar1 = unit.declarations[1]; |
| FunctionDeclaration bar2 = unit.declarations[2]; |
| |
| _assertPrevNextToken(foo.endToken, bar1.beginToken); |
| _assertPrevNextToken(bar1.endToken, bar2.beginToken); |
| } |
| |
| test_topLevel_function_fail_noExternalKeyword() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| int foo(); |
| ''', r''' |
| @patch |
| int foo() => 1; |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_function_fail_notPrivate() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| int foo() => 1; |
| ''', r''' |
| int bar() => 2; |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_functionTypeAlias_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| int foo() => 0; |
| ''', r''' |
| typedef int _bar1(); |
| typedef int _bar2(); |
| '''); |
| _assertUnitCode( |
| unit, 'int foo() => 0; typedef int _bar1(); typedef int _bar2();'); |
| |
| FunctionDeclaration foo = unit.declarations[0]; |
| FunctionTypeAlias bar1 = unit.declarations[1]; |
| FunctionTypeAlias bar2 = unit.declarations[2]; |
| |
| _assertPrevNextToken(foo.endToken, bar1.beginToken); |
| _assertPrevNextToken(bar1.endToken, bar2.beginToken); |
| expect(unit.endToken.type, TokenType.EOF); |
| expect(bar2.endToken.next, same(unit.endToken)); |
| } |
| |
| test_topLevel_functionTypeAlias_fail_hasAnnotation() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| int foo() => 0; |
| ''', r''' |
| @patch |
| typedef int _bar(); |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_functionTypeAlias_fail_notPrivate() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| int foo() => 0; |
| ''', r''' |
| typedef int bar(); |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_patch_function() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| external int foo(); |
| int bar() => 2; |
| ''', r''' |
| @patch |
| int foo() => 1; |
| '''); |
| _assertUnitCode(unit, 'int foo() => 1; int bar() => 2;'); |
| |
| // Prepare functions. |
| FunctionDeclaration foo = unit.declarations[0]; |
| FunctionDeclaration bar = unit.declarations[1]; |
| |
| // The "external" token is removed from the stream. |
| { |
| expect(foo.externalKeyword, isNull); |
| Token token = foo.beginToken; |
| expect(token.lexeme, 'int'); |
| expect(token.previous.type, TokenType.EOF); |
| } |
| |
| // The body tokens are included into the patched token stream. |
| { |
| FunctionExpression fooExpr = foo.functionExpression; |
| FunctionBody fooBody = fooExpr.body; |
| expect(fooBody.beginToken.previous, same(fooExpr.parameters.endToken)); |
| expect(fooBody.endToken.next, same(bar.beginToken)); |
| } |
| } |
| |
| test_topLevel_patch_function_blockBody() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| external int foo(); |
| ''', r''' |
| @patch |
| int foo() {int v = 1; return v + 2;} |
| '''); |
| _assertUnitCode(unit, 'int foo() {int v = 1; return v + 2;}'); |
| } |
| |
| test_topLevel_patch_function_fail_signatureChange() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| external void f(int x); |
| ''', r''' |
| @patch |
| void f(double x) {} |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_patch_function_fail_signatureChange_nameOnly() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| external void f(int x); |
| ''', r''' |
| @patch |
| void f(int y) {} |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_patch_function_fail_signatureChange_returnTypeOnly() { |
| expect(() { |
| _doTopLevelPatching(r''' |
| external void f(int x); |
| ''', r''' |
| @patch |
| int f(int x) {} |
| '''); |
| }, throwsArgumentError); |
| } |
| |
| test_topLevel_patch_getter() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| external int get foo; |
| int bar() => 2; |
| ''', r''' |
| @patch |
| int get foo => 1; |
| '''); |
| _assertUnitCode(unit, 'int get foo => 1; int bar() => 2;'); |
| } |
| |
| test_topLevel_patch_setter() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| external void set foo(int val); |
| int bar() => 2; |
| ''', r''' |
| @patch |
| void set foo(int val) {} |
| '''); |
| _assertUnitCode(unit, 'void set foo(int val) {} int bar() => 2;'); |
| } |
| |
| test_topLevel_topLevelVariable_append() { |
| CompilationUnit unit = _doTopLevelPatching(r''' |
| int foo() => 0; |
| ''', r''' |
| int _bar; |
| '''); |
| _assertUnitCode(unit, 'int foo() => 0; int _bar;'); |
| FunctionDeclaration a = unit.declarations[0]; |
| TopLevelVariableDeclaration b = unit.declarations[1]; |
| _assertPrevNextToken(a.endToken, b.beginToken); |
| } |
| |
| void _assertUnitCode(CompilationUnit unit, String expectedCode) { |
| expect(unit.toSource(), expectedCode); |
| } |
| |
| void _createSdk() { |
| sdk = new FolderBasedDartSdk(provider, sdkFolder); |
| sdk.analysisOptions = new AnalysisOptionsImpl()..strongMode = true; |
| } |
| |
| CompilationUnit _doTopLevelPatching(String baseCode, String patchCode) { |
| _setSdkLibraries(r''' |
| final Map<String, LibraryInfo> LIBRARIES = const <String, LibraryInfo> { |
| 'test' : const LibraryInfo( |
| 'test/test.dart'), |
| };'''); |
| var patchPaths = { |
| 'dart:test': [_p('/sdk/lib/test/test_patch.dart')] |
| }; |
| File file = provider.newFile(_p('/sdk/lib/test/test.dart'), baseCode); |
| provider.newFile(_p('/sdk/lib/test/test_patch.dart'), patchCode); |
| |
| _createSdk(); |
| |
| Source source = file.createSource(Uri.parse('dart:test')); |
| CompilationUnit unit = SdkPatcher.parse(source, true, listener); |
| patcher.patch(provider, true, patchPaths, listener, source, unit); |
| return unit; |
| } |
| |
| String _p(String path) => provider.convertPath(path); |
| |
| void _setSdkLibraries(String code) { |
| provider.newFile( |
| _p('/sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart'), code); |
| } |
| |
| static void _assertPrevNextToken(Token prev, Token next) { |
| expect(prev.next, same(next)); |
| expect(next.previous, same(prev)); |
| } |
| } |