| // Copyright (c) 2021, 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:math'; |
| |
| import 'package:_macros/src/api.dart'; |
| import 'package:_macros/src/executor.dart'; |
| import 'package:_macros/src/executor/exception_impls.dart'; |
| import 'package:_macros/src/executor/introspection_impls.dart'; |
| import 'package:_macros/src/executor/remote_instance.dart'; |
| import 'package:_macros/src/executor/serialization.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../util.dart'; |
| |
| void main() { |
| // We randomize fields which should make the tests more likely to catch issues |
| // related to serialization ordering. |
| final seed = Random().nextInt(1000); |
| print('Nondeterministic test ran with seed: $seed, change to this seed to ' |
| 'repro.'); |
| final rand = Random(seed); |
| |
| for (var mode in [SerializationMode.json, SerializationMode.byteData]) { |
| test('$mode can serialize and deserialize basic data', () { |
| withSerializationMode(mode, () { |
| var serializer = serializerFactory(); |
| serializer |
| ..addInt(0) |
| ..addInt(1) |
| ..addInt(0xff) |
| ..addInt(0xffff) |
| ..addInt(0xffffffff) |
| ..addInt(0xffffffffffffffff) |
| ..addInt(-1) |
| ..addInt(-0x80) |
| ..addInt(-0x8000) |
| ..addInt(-0x80000000) |
| ..addInt(-0x8000000000000000) |
| ..addNullableInt(null) |
| ..addString('hello') |
| ..addString('€') // Requires a two byte string |
| ..addString('𐐷') // Requires two, 16 bit code units |
| ..addNullableString(null) |
| ..startList() |
| ..addBool(true) |
| ..startList() |
| ..addNull() |
| ..endList() |
| ..addNullableBool(null) |
| ..endList() |
| ..addDouble(1.0) |
| ..startList() |
| ..endList(); |
| var deserializer = deserializerFactory(serializer.result); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 0); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 1); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 0xff); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 0xffff); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 0xffffffff); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), 0xffffffffffffffff); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), -1); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), -0x80); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), -0x8000); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), -0x80000000); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectInt(), -0x8000000000000000); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectNullableInt(), null); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectString(), 'hello'); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectString(), '€'); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectString(), '𐐷'); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectNullableString(), null); |
| expect(deserializer.moveNext(), true); |
| |
| deserializer.expectList(); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectBool(), true); |
| expect(deserializer.moveNext(), true); |
| |
| deserializer.expectList(); |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.checkNull(), true); |
| expect(deserializer.moveNext(), false); |
| |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectNullableBool(), null); |
| expect(deserializer.moveNext(), false); |
| |
| // Have to move the parent again to advance it past the list entry. |
| expect(deserializer.moveNext(), true); |
| expect(deserializer.expectDouble(), 1.0); |
| expect(deserializer.moveNext(), true); |
| |
| deserializer.expectList(); |
| expect(deserializer.moveNext(), false); |
| |
| expect(deserializer.moveNext(), false); |
| }); |
| }); |
| } |
| |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| test('remote instances in $mode', () async { |
| var string = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: false, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'String'), |
| typeArguments: const []); |
| var foo = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: false, |
| identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'), |
| typeArguments: [string]); |
| |
| withSerializationMode(mode, () { |
| final int zoneId = newRemoteInstanceZone(); |
| withRemoteInstanceZone(zoneId, () { |
| var serializer = serializerFactory(); |
| foo.serialize(serializer); |
| // This is a fake client, we don't want to actually share the cache, |
| // so we negate the zone id and use that. |
| var response = roundTrip(serializer.result, -zoneId); |
| var deserializer = deserializerFactory(response); |
| var instance = RemoteInstance.deserialize(deserializer); |
| expect(instance, foo); |
| }); |
| }); |
| }); |
| } |
| |
| group('declarations', () { |
| final barType = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: rand.nextBool(), |
| identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Bar'), |
| typeArguments: []); |
| final fooType = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: rand.nextBool(), |
| identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'), |
| typeArguments: [barType]); |
| |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| group('with mode $mode', () { |
| test('NamedTypeAnnotation', () { |
| expectSerializationEquality<TypeAnnotationImpl>( |
| fooType, mode, RemoteInstance.deserialize); |
| }); |
| |
| final fooNamedParam = FormalParameterDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| isNamed: rand.nextBool(), |
| isRequired: rand.nextBool(), |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'), |
| library: Fixtures.library, |
| metadata: [], |
| style: ParameterStyle.values[rand.nextInt(3)], |
| type: fooType); |
| final fooNamedFunctionTypeParam = FormalParameterImpl( |
| id: RemoteInstance.uniqueId, |
| isNamed: rand.nextBool(), |
| isRequired: rand.nextBool(), |
| metadata: [], |
| name: 'foo', |
| type: fooType); |
| |
| final barPositionalParam = FormalParameterDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| isNamed: rand.nextBool(), |
| isRequired: rand.nextBool(), |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'), |
| library: Fixtures.library, |
| metadata: [], |
| style: ParameterStyle.values[rand.nextInt(3)], |
| type: barType); |
| final barPositionalFunctionTypeParam = FormalParameterImpl( |
| id: RemoteInstance.uniqueId, |
| isNamed: rand.nextBool(), |
| isRequired: rand.nextBool(), |
| metadata: [], |
| name: 'bar', |
| type: fooType); |
| |
| final unnamedFunctionTypeParam = FormalParameterImpl( |
| id: RemoteInstance.uniqueId, |
| isNamed: rand.nextBool(), |
| isRequired: rand.nextBool(), |
| metadata: [], |
| name: rand.nextBool() ? null : 'zip', |
| type: fooType); |
| |
| final zapTypeParam = TypeParameterDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Zap'), |
| library: Fixtures.library, |
| metadata: [], |
| bound: barType); |
| |
| // Transitively tests `TypeParameterDeclaration` and |
| // `ParameterDeclaration`. |
| test('FunctionTypeAnnotation', () { |
| var functionType = FunctionTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: rand.nextBool(), |
| namedParameters: [ |
| fooNamedFunctionTypeParam, |
| unnamedFunctionTypeParam |
| ], |
| positionalParameters: [barPositionalFunctionTypeParam], |
| returnType: fooType, |
| typeParameters: [ |
| TypeParameterImpl( |
| id: RemoteInstance.uniqueId, |
| metadata: [], |
| name: 'Zip', |
| bound: barType) |
| ], |
| ); |
| expectSerializationEquality<TypeAnnotationImpl>( |
| functionType, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('FunctionDeclaration', () { |
| var function = FunctionDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'name'), |
| library: Fixtures.library, |
| metadata: [], |
| hasBody: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| isGetter: rand.nextBool(), |
| isOperator: rand.nextBool(), |
| isSetter: rand.nextBool(), |
| namedParameters: [], |
| positionalParameters: [], |
| returnType: fooType, |
| typeParameters: []); |
| expectSerializationEquality<DeclarationImpl>( |
| function, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('MethodDeclaration', () { |
| var method = MethodDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'zorp'), |
| library: Fixtures.library, |
| metadata: [], |
| hasBody: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| isGetter: rand.nextBool(), |
| isOperator: rand.nextBool(), |
| isSetter: rand.nextBool(), |
| namedParameters: [fooNamedParam], |
| positionalParameters: [barPositionalParam], |
| returnType: fooType, |
| typeParameters: [zapTypeParam], |
| definingType: fooType.identifier, |
| hasStatic: rand.nextBool()); |
| expectSerializationEquality<DeclarationImpl>( |
| method, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('ConstructorDeclaration', () { |
| var constructor = ConstructorDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'new'), |
| library: Fixtures.library, |
| metadata: [], |
| hasBody: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| namedParameters: [fooNamedParam], |
| positionalParameters: [barPositionalParam], |
| returnType: fooType, |
| typeParameters: [zapTypeParam], |
| definingType: fooType.identifier, |
| isConst: rand.nextBool(), |
| isFactory: rand.nextBool(), |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| constructor, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('VariableDeclaration', () { |
| var bar = VariableDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'), |
| library: Fixtures.library, |
| metadata: [], |
| hasConst: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| hasFinal: rand.nextBool(), |
| hasInitializer: rand.nextBool(), |
| hasLate: rand.nextBool(), |
| type: barType, |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| bar, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('FieldDeclaration', () { |
| var bar = FieldDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'), |
| library: Fixtures.library, |
| metadata: [], |
| hasAbstract: rand.nextBool(), |
| hasConst: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| hasFinal: rand.nextBool(), |
| hasInitializer: rand.nextBool(), |
| hasLate: rand.nextBool(), |
| type: barType, |
| definingType: fooType.identifier, |
| hasStatic: rand.nextBool(), |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| bar, mode, RemoteInstance.deserialize); |
| }); |
| |
| var objectType = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Object'), |
| isNullable: false, |
| typeArguments: [], |
| ); |
| var serializableType = NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Serializable'), |
| isNullable: rand.nextBool(), |
| typeArguments: [], |
| ); |
| |
| test('ClassDeclaration', () { |
| var fooClass = ClassDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'), |
| library: Fixtures.library, |
| metadata: [], |
| interfaces: [barType], |
| hasAbstract: rand.nextBool(), |
| hasBase: rand.nextBool(), |
| hasExternal: rand.nextBool(), |
| hasFinal: rand.nextBool(), |
| hasInterface: rand.nextBool(), |
| hasMixin: rand.nextBool(), |
| hasSealed: rand.nextBool(), |
| mixins: [serializableType], |
| superclass: objectType, |
| typeParameters: [zapTypeParam], |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| fooClass, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('EnumDeclaration', () { |
| var fooEnum = EnumDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyEnum'), |
| library: Fixtures.library, |
| metadata: [], |
| interfaces: [barType], |
| mixins: [serializableType], |
| typeParameters: [zapTypeParam], |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| fooEnum, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('EnumValueDeclaration', () { |
| var entry = EnumValueDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: IdentifierImpl(id: RemoteInstance.uniqueId, name: 'a'), |
| library: Fixtures.library, |
| metadata: [], |
| definingEnum: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyEnum'), |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| entry, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('ExtensionDeclaration', () { |
| var extension = ExtensionDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: IdentifierImpl( |
| id: RemoteInstance.uniqueId, name: 'MyExtension'), |
| library: Fixtures.library, |
| metadata: [], |
| typeParameters: [], |
| onType: Fixtures.myClassType); |
| expectSerializationEquality<DeclarationImpl>( |
| extension, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('ExtensionTypeDeclaration', () { |
| var extensionType = ExtensionTypeDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: IdentifierImpl( |
| id: RemoteInstance.uniqueId, name: 'MyExtensionType'), |
| library: Fixtures.library, |
| metadata: [], |
| typeParameters: [], |
| representationType: Fixtures.myClassType); |
| expectSerializationEquality<DeclarationImpl>( |
| extensionType, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('MixinDeclaration', () { |
| var mixin = MixinDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'MyMixin'), |
| library: Fixtures.library, |
| metadata: [], |
| hasBase: rand.nextBool(), |
| interfaces: [barType], |
| superclassConstraints: [serializableType], |
| typeParameters: [zapTypeParam], |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| mixin, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('TypeAliasDeclaration', () { |
| var typeAlias = TypeAliasDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'FooOfBar'), |
| library: Fixtures.library, |
| metadata: [], |
| typeParameters: [zapTypeParam], |
| aliasedType: NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: rand.nextBool(), |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'), |
| typeArguments: [barType]), |
| ); |
| expectSerializationEquality<DeclarationImpl>( |
| typeAlias, mode, RemoteInstance.deserialize); |
| }); |
| |
| /// Transitively tests [RecordField] |
| test('RecordTypeAnnotation', () { |
| var recordType = RecordTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: rand.nextBool(), |
| namedFields: [ |
| RecordFieldImpl( |
| id: RemoteInstance.uniqueId, |
| name: 'hello', |
| type: barType, |
| ), |
| ], |
| positionalFields: [ |
| RecordFieldImpl( |
| id: RemoteInstance.uniqueId, |
| name: rand.nextBool() ? null : 'zoiks', |
| type: fooType, |
| ), |
| ], |
| ); |
| expectSerializationEquality<TypeAnnotationImpl>( |
| recordType, mode, RemoteInstance.deserialize); |
| }); |
| }); |
| } |
| }); |
| |
| group('Arguments', () { |
| test('can create properly typed collections', () { |
| withSerializationMode(SerializationMode.json, () { |
| final parsed = Arguments.deserialize(deserializerFactory([ |
| // positional args |
| [ |
| // int |
| ArgumentKind.int.index, |
| 1, |
| // List<int> |
| ArgumentKind.list.index, |
| [ArgumentKind.int.index], |
| [ |
| ArgumentKind.int.index, |
| 1, |
| ArgumentKind.int.index, |
| 2, |
| ArgumentKind.int.index, |
| 3, |
| ], |
| // List<Set<String>> |
| ArgumentKind.list.index, |
| [ArgumentKind.set.index, ArgumentKind.string.index], |
| [ |
| // Set<String> |
| ArgumentKind.set.index, |
| [ArgumentKind.string.index], |
| [ |
| ArgumentKind.string.index, |
| 'hello', |
| ArgumentKind.string.index, |
| 'world', |
| ] |
| ], |
| // Map<int, List<String>> |
| ArgumentKind.map.index, |
| [ |
| ArgumentKind.int.index, |
| ArgumentKind.nullable.index, |
| ArgumentKind.list.index, |
| ArgumentKind.string.index |
| ], |
| [ |
| // key: int |
| ArgumentKind.int.index, |
| 4, |
| // value: List<String> |
| ArgumentKind.list.index, |
| [ArgumentKind.string.index], |
| [ |
| ArgumentKind.string.index, |
| 'zip', |
| ], |
| ArgumentKind.int.index, |
| 5, |
| ArgumentKind.nil.index, |
| ] |
| ], |
| // named args |
| [], |
| ])); |
| expect(parsed.positional.length, 4); |
| expect(parsed.positional.first.value, 1); |
| expect(parsed.positional[1].value, [1, 2, 3]); |
| expect(parsed.positional[1].value, isA<List<int>>()); |
| expect(parsed.positional[2].value, [ |
| {'hello', 'world'} |
| ]); |
| expect(parsed.positional[2].value, isA<List<Set<String>>>()); |
| expect( |
| parsed.positional[3].value, |
| { |
| 4: ['zip'], |
| 5: null, |
| }, |
| ); |
| expect(parsed.positional[3].value, isA<Map<int, List<String>?>>()); |
| }); |
| }); |
| |
| group('can be serialized and deserialized', () { |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| test('with mode $mode', () { |
| final arguments = Arguments([ |
| MapArgument({ |
| StringArgument('hello'): ListArgument( |
| [BoolArgument(rand.nextBool()), NullArgument()], |
| [ArgumentKind.nullable, ArgumentKind.bool]), |
| }, [ |
| ArgumentKind.string, |
| ArgumentKind.list, |
| ArgumentKind.nullable, |
| ArgumentKind.bool |
| ]), |
| CodeArgument(ExpressionCode.fromParts([ |
| '1 + ', |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'a') |
| ])), |
| ListArgument([ |
| TypeAnnotationArgument(Fixtures.myClassType), |
| TypeAnnotationArgument(Fixtures.myEnumType), |
| TypeAnnotationArgument(NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| isNullable: false, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'List'), |
| typeArguments: [Fixtures.stringType])), |
| ], [ |
| ArgumentKind.typeAnnotation |
| ]) |
| ], { |
| 'a': SetArgument([ |
| MapArgument({ |
| IntArgument(1): StringArgument('1'), |
| }, [ |
| ArgumentKind.int, |
| ArgumentKind.string |
| ]) |
| ], [ |
| ArgumentKind.map, |
| ArgumentKind.int, |
| ArgumentKind.string |
| ]) |
| }); |
| expectSerializationEquality(arguments, mode, Arguments.deserialize); |
| }); |
| } |
| }); |
| }); |
| |
| group('Exceptions', () { |
| group('can be serialized and deserialized', () { |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| test('with mode $mode', () { |
| final exception = UnexpectedMacroExceptionImpl('something happened', |
| stackTrace: 'here'); |
| expectSerializationEquality<UnexpectedMacroExceptionImpl>( |
| exception, mode, RemoteInstance.deserialize); |
| }); |
| } |
| }); |
| }); |
| |
| group('metadata annotations can be serialized and deserialized', () { |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| group('with mode $mode', () { |
| test('identifiers', () { |
| final identifierMetadata = IdentifierMetadataAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: IdentifierImpl( |
| id: RemoteInstance.uniqueId, name: 'singleton')); |
| |
| expectSerializationEquality<IdentifierMetadataAnnotationImpl>( |
| identifierMetadata, mode, RemoteInstance.deserialize); |
| }); |
| |
| test('constructor invocations', () { |
| final constructorMetadata = ConstructorMetadataAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| type: NamedTypeAnnotationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: IdentifierImpl( |
| id: RemoteInstance.uniqueId, name: 'Singleton'), |
| isNullable: false, |
| typeArguments: []), |
| constructor: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'someName'), |
| positionalArguments: [ |
| ExpressionCode.fromString("'foo'"), |
| ExpressionCode.fromString('12'), |
| ], |
| namedArguments: { |
| 'bar': ExpressionCode.fromString("'bar'"), |
| 'foobar': ExpressionCode.fromString('13'), |
| }); |
| |
| expectSerializationEquality<ConstructorMetadataAnnotationImpl>( |
| constructorMetadata, mode, RemoteInstance.deserialize); |
| }); |
| }); |
| } |
| }); |
| |
| group('static types can be serialized and deserialized', () { |
| for (var mode in [SerializationMode.byteData, SerializationMode.json]) { |
| group('with mode $mode', () { |
| test('named static type', () async { |
| final staticType = NamedStaticTypeImpl( |
| RemoteInstance.uniqueId, |
| declaration: ClassDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'Foo'), |
| library: Fixtures.library, |
| metadata: [], |
| interfaces: [], |
| hasAbstract: false, |
| hasBase: false, |
| hasExternal: false, |
| hasFinal: false, |
| hasInterface: false, |
| hasMixin: false, |
| hasSealed: false, |
| mixins: [], |
| superclass: null, |
| typeParameters: [ |
| TypeParameterDeclarationImpl( |
| id: RemoteInstance.uniqueId, |
| identifier: |
| IdentifierImpl(id: RemoteInstance.uniqueId, name: 'T'), |
| library: Fixtures.library, |
| metadata: const [], |
| bound: null, |
| ), |
| ], |
| ), |
| typeArguments: [ |
| NamedStaticTypeImpl( |
| RemoteInstance.uniqueId, |
| declaration: Fixtures.stringClass, |
| typeArguments: const [], |
| ), |
| ], |
| ); |
| expectSerializationEquality<NamedStaticTypeImpl>( |
| staticType, mode, RemoteInstance.deserialize); |
| }); |
| }); |
| } |
| }); |
| } |
| |
| /// Serializes [serializable] in server mode, then deserializes it in client |
| /// mode, and checks that all the fields are the same. |
| void expectSerializationEquality<T extends Serializable>(T serializable, |
| SerializationMode mode, T Function(Deserializer deserializer) deserialize) { |
| withSerializationMode(mode, () { |
| late Object? serialized; |
| final int zoneId = newRemoteInstanceZone(); |
| withRemoteInstanceZone(zoneId, () { |
| var serializer = serializerFactory(); |
| serializable.serialize(serializer); |
| serialized = serializer.result; |
| }); |
| |
| // This is a fake client, we don't want to actually share the cache, |
| // so we negate the zone id and use that. |
| withRemoteInstanceZone(-zoneId, () { |
| var deserializer = deserializerFactory(serialized); |
| var deserialized = deserialize(deserializer); |
| |
| expect( |
| serializable, |
| switch (deserialized) { |
| Declaration() => deepEqualsDeclaration(deserialized as Declaration), |
| TypeAnnotation() => |
| deepEqualsTypeAnnotation(deserialized as TypeAnnotation), |
| Arguments() => deepEqualsArguments(deserialized), |
| MacroExceptionImpl() => deepEqualsMacroException(deserialized), |
| MetadataAnnotation() => |
| deepEqualsMetadataAnnotation(deserialized as MetadataAnnotation), |
| NamedStaticTypeImpl() => |
| deepEqualsStaticType(deserialized as NamedStaticTypeImpl), |
| _ => |
| throw UnsupportedError('Unsupported object type $deserialized'), |
| }); |
| }, createIfMissing: true); |
| }); |
| } |
| |
| /// Deserializes [serialized] in its own remote instance cache and sends it |
| /// back. |
| Object? roundTrip(Object? serialized, int zoneId) { |
| return withRemoteInstanceZone(zoneId, () { |
| var deserializer = deserializerFactory(serialized); |
| var instance = RemoteInstance.deserialize(deserializer) as Serializable; |
| var serializer = serializerFactory(); |
| instance.serialize(serializer); |
| return serializer.result; |
| }, createIfMissing: true); |
| } |