diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 5bca85da4b69..6be42dca77c6 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -4,6 +4,7 @@ * [front-end] Added a more explicit error for static fields. * [front-end] Added more errors for incorrect usage of Pigeon (previously they were just ignored). * Moved Pigeon to using a custom codec which allows collection types to contain custom classes. +* Started allowing primitive data types as arguments and return types. ## 0.3.0 diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 8e6d59b994ee..c2311352b8a4 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -121,19 +121,14 @@ final BinaryMessenger$nullTag _binaryMessenger; } String argSignature = ''; String sendArgument = 'null'; - String? encodedDeclaration; if (func.argType != 'void') { argSignature = '${func.argType} arg'; - sendArgument = 'encoded'; - encodedDeclaration = 'final Object encoded = arg.encode();'; + sendArgument = 'arg'; } indent.write( 'Future<${func.returnType}> ${func.name}($argSignature) async ', ); indent.scoped('{', '}', () { - if (encodedDeclaration != null) { - indent.writeln(encodedDeclaration); - } final String channelName = makeChannelName(api, func); indent.writeln( 'final BasicMessageChannel channel = BasicMessageChannel('); @@ -144,7 +139,7 @@ final BinaryMessenger$nullTag _binaryMessenger; }); final String returnStatement = func.returnType == 'void' ? '// noop' - : 'return ${func.returnType}.decode(replyMap[\'${Keys.result}\']$unwrapOperator);'; + : 'return (replyMap[\'${Keys.result}\'] as ${func.returnType}$nullTag)$unwrapOperator;'; indent.format(''' final Map$nullTag replyMap =\n\t\tawait channel.send($sendArgument) as Map$nullTag; if (replyMap == null) { @@ -237,7 +232,7 @@ void _writeFlutterApi( 'assert(message != null, \'Argument for $channelName was null. Expected $argType.\');', ); indent.writeln( - 'final $argType input = $argType.decode(message$unwrapOperator);', + 'final $argType input = (message as $argType$nullTag)$unwrapOperator;', ); call = 'api.${func.name}(input)'; } @@ -254,7 +249,7 @@ void _writeFlutterApi( } else { indent.writeln('final $returnType output = $call;'); } - const String returnExpression = 'output.encode()'; + const String returnExpression = 'output'; final String returnStatement = isMockHandler ? 'return {\'${Keys.result}\': $returnExpression};' : 'return $returnExpression;'; diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index e5f07d9f45fd..560bcd184fbd 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -262,6 +262,20 @@ class EnumeratedClass { final int enumeration; } +/// Supported basic datatypes. +const List validTypes = [ + 'String', + 'bool', + 'int', + 'double', + 'Uint8List', + 'Int32List', + 'Int64List', + 'Float64List', + 'List', + 'Map', +]; + /// Custom codecs' custom types are enumerated from 255 down to this number to /// avoid collisions with the StandardMessageCodec. const int _minimumCodecFieldKey = 128; @@ -274,8 +288,10 @@ Iterable getCodecClasses(Api api) sync* { names.add(method.returnType); names.add(method.argType); } - final List sortedNames = - names.where((String element) => element != 'void').toList(); + final List sortedNames = names + .where((String element) => + element != 'void' && !validTypes.contains(element)) + .toList(); sortedNames.sort(); int enumeration = _minimumCodecFieldKey; const int maxCustomClassesPerApi = 255 - _minimumCodecFieldKey; diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart index d53c8293c6ac..eae2cea23567 100644 --- a/packages/pigeon/lib/java_generator.dart +++ b/packages/pigeon/lib/java_generator.dart @@ -113,6 +113,22 @@ void _writeCodec(Indent indent, Api api) { }); } +/// This performs Dart to Java type conversions. If performs a passthrough of +/// the input if it can't be converted. +// TODO(gaaclarke): Remove this method and unify it with `_javaTypeForDartType`. +String _javaTypeForDartTypePassthrough(String type) { + const Map map = { + 'int': 'Integer', + 'bool': 'Boolean', + 'double': 'Double', + 'Int32List': 'int[]', + 'Uint8List': 'byte[]', + 'Int64List': 'long[]', + 'Float64List': 'double[]', + }; + return map[type] ?? type; +} + void _writeHostApi(Indent indent, Api api) { assert(api.location == ApiLocation.host); @@ -121,11 +137,13 @@ void _writeHostApi(Indent indent, Api api) { indent.write('public interface ${api.name} '); indent.scoped('{', '}', () { for (final Method method in api.methods) { - final String returnType = - method.isAsynchronous ? 'void' : method.returnType; + final String argType = _javaTypeForDartTypePassthrough(method.argType); + final String returnType = method.isAsynchronous + ? 'void' + : _javaTypeForDartTypePassthrough(method.returnType); final List argSignature = []; if (method.argType != 'void') { - argSignature.add('${method.argType} arg'); + argSignature.add('$argType arg'); } if (method.isAsynchronous) { final String returnType = @@ -162,21 +180,27 @@ static MessageCodec getCodec() { indent.scoped('{', '} else {', () { indent.write('channel.setMessageHandler((message, reply) -> '); indent.scoped('{', '});', () { - final String argType = method.argType; - final String returnType = method.returnType; + final String argType = + _javaTypeForDartTypePassthrough(method.argType); + final String returnType = + _javaTypeForDartTypePassthrough(method.returnType); indent.writeln('Map wrapped = new HashMap<>();'); indent.write('try '); indent.scoped('{', '}', () { final List methodArgument = []; if (argType != 'void') { indent.writeln('@SuppressWarnings("ConstantConditions")'); - indent.writeln( - '$argType input = $argType.fromMap((Map)message);'); + indent.writeln('$argType input = ($argType)message;'); + indent.write('if (input == null) '); + indent.scoped('{', '}', () { + indent.writeln( + 'throw new NullPointerException("Message unexpectedly null.");'); + }); methodArgument.add('input'); } if (method.isAsynchronous) { final String resultValue = - method.returnType == 'void' ? 'null' : 'result.toMap()'; + method.returnType == 'void' ? 'null' : 'result'; methodArgument.add( 'result -> { ' 'wrapped.put("${Keys.result}", $resultValue); ' @@ -193,8 +217,7 @@ static MessageCodec getCodec() { indent.writeln('wrapped.put("${Keys.result}", null);'); } else { indent.writeln('$returnType output = $call;'); - indent.writeln( - 'wrapped.put("${Keys.result}", output.toMap());'); + indent.writeln('wrapped.put("${Keys.result}", output);'); } }); indent.write('catch (Error | RuntimeException exception) '); @@ -242,16 +265,18 @@ static MessageCodec getCodec() { '''); for (final Method func in api.methods) { final String channelName = makeChannelName(api, func); - final String returnType = - func.returnType == 'void' ? 'Void' : func.returnType; + final String returnType = func.returnType == 'void' + ? 'Void' + : _javaTypeForDartTypePassthrough(func.returnType); + final String argType = _javaTypeForDartTypePassthrough(func.argType); String sendArgument; if (func.argType == 'void') { indent.write('public void ${func.name}(Reply<$returnType> callback) '); sendArgument = 'null'; } else { indent.write( - 'public void ${func.name}(${func.argType} argInput, Reply<$returnType> callback) '); - sendArgument = 'inputMap'; + 'public void ${func.name}($argType argInput, Reply<$returnType> callback) '); + sendArgument = 'argInput'; } indent.scoped('{', '}', () { indent.writeln('BasicMessageChannel channel ='); @@ -261,18 +286,13 @@ static MessageCodec getCodec() { 'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());'); indent.dec(); indent.dec(); - if (func.argType != 'void') { - indent.writeln('Map inputMap = argInput.toMap();'); - } indent.write('channel.send($sendArgument, channelReply -> '); indent.scoped('{', '});', () { if (func.returnType == 'void') { indent.writeln('callback.reply(null);'); } else { - indent.writeln('Map outputMap = (Map)channelReply;'); indent.writeln('@SuppressWarnings("ConstantConditions")'); - indent.writeln( - '${func.returnType} output = ${func.returnType}.fromMap(outputMap);'); + indent.writeln('$returnType output = ($returnType)channelReply;'); indent.writeln('callback.reply(output);'); } }); diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index c46708fbe391..77612a49854f 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -67,16 +67,16 @@ String _callbackForType(String dartType, String objcType) { } const Map _objcTypeForDartTypeMap = { - 'bool': 'NSNumber *', - 'int': 'NSNumber *', - 'String': 'NSString *', - 'double': 'NSNumber *', - 'Uint8List': 'FlutterStandardTypedData *', - 'Int32List': 'FlutterStandardTypedData *', - 'Int64List': 'FlutterStandardTypedData *', - 'Float64List': 'FlutterStandardTypedData *', - 'List': 'NSArray *', - 'Map': 'NSDictionary *', + 'bool': 'NSNumber', + 'int': 'NSNumber', + 'String': 'NSString', + 'double': 'NSNumber', + 'Uint8List': 'FlutterStandardTypedData', + 'Int32List': 'FlutterStandardTypedData', + 'Int64List': 'FlutterStandardTypedData', + 'Float64List': 'FlutterStandardTypedData', + 'List': 'NSArray', + 'Map': 'NSDictionary', }; const Map _propertyTypeForDartTypeMap = { @@ -92,8 +92,18 @@ const Map _propertyTypeForDartTypeMap = { 'Map': 'strong', }; -String? _objcTypeForDartType(String type) { - return _objcTypeForDartTypeMap[type]; +String? _objcTypePtrForPrimitiveDartType(String type) { + return _objcTypeForDartTypeMap.containsKey(type) + ? '${_objcTypeForDartTypeMap[type]} *' + : null; +} + +/// Returns the objc type for a dart [type], prepending the [classPrefix] for +/// generated classes. For example: +/// _objcTypeForDartType(null, 'int') => 'NSNumber'. +String _objcTypeForDartType(String? classPrefix, String type) { + final String? builtinObjcType = _objcTypeForDartTypeMap[type]; + return builtinObjcType ?? _className(classPrefix, type); } String _propertyTypeForDartType(String type) { @@ -112,7 +122,7 @@ void _writeClassDeclarations( indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject'); for (final Field field in klass.fields) { final HostDatatype hostDatatype = getHostDatatype( - field, classes, enums, _objcTypeForDartType, + field, classes, enums, _objcTypePtrForPrimitiveDartType, customResolver: enumNames.contains(field.dataType) ? (String x) => _className(prefix, x) : (String x) => '${_className(prefix, x)} *'); @@ -217,14 +227,16 @@ void _writeHostApiDeclaration(Indent indent, Api api, ObjcOptions options) { final String apiName = _className(options.prefix, api.name); indent.writeln('@protocol $apiName'); for (final Method func in api.methods) { - final String returnTypeName = _className(options.prefix, func.returnType); + final String returnTypeName = + _objcTypeForDartType(options.prefix, func.returnType); if (func.isAsynchronous) { if (func.returnType == 'void') { if (func.argType == 'void') { indent.writeln( '-(void)${func.name}:(void(^)(FlutterError *_Nullable))completion;'); } else { - final String argType = _className(options.prefix, func.argType); + final String argType = + _objcTypeForDartType(options.prefix, func.argType); indent.writeln( '-(void)${func.name}:(nullable $argType *)input completion:(void(^)(FlutterError *_Nullable))completion;'); } @@ -233,7 +245,8 @@ void _writeHostApiDeclaration(Indent indent, Api api, ObjcOptions options) { indent.writeln( '-(void)${func.name}:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;'); } else { - final String argType = _className(options.prefix, func.argType); + final String argType = + _objcTypeForDartType(options.prefix, func.argType); indent.writeln( '-(void)${func.name}:(nullable $argType *)input completion:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;'); } @@ -245,7 +258,8 @@ void _writeHostApiDeclaration(Indent indent, Api api, ObjcOptions options) { indent.writeln( '-($returnType)${func.name}:(FlutterError *_Nullable *_Nonnull)error;'); } else { - final String argType = _className(options.prefix, func.argType); + final String argType = + _objcTypeForDartType(options.prefix, func.argType); indent.writeln( '-($returnType)${func.name}:($argType*)input error:(FlutterError *_Nullable *_Nonnull)error;'); } @@ -264,12 +278,13 @@ void _writeFlutterApiDeclaration(Indent indent, Api api, ObjcOptions options) { indent.writeln( '- (instancetype)initWithBinaryMessenger:(id)binaryMessenger;'); for (final Method func in api.methods) { - final String returnType = _className(options.prefix, func.returnType); + final String returnType = + _objcTypeForDartType(options.prefix, func.returnType); final String callbackType = _callbackForType(func.returnType, returnType); if (func.argType == 'void') { indent.writeln('- (void)${func.name}:($callbackType)completion;'); } else { - final String argType = _className(options.prefix, func.argType); + final String argType = _objcTypeForDartType(options.prefix, func.argType); indent.writeln( '- (void)${func.name}:($argType*)input completion:($callbackType)completion;'); } @@ -386,13 +401,14 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) { '[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) '); indent.scoped('{', '}];', () { final String returnType = - _className(options.prefix, func.returnType); + _objcTypeForDartType(options.prefix, func.returnType); String syncCall; if (func.argType == 'void') { syncCall = '[api ${func.name}:&error]'; } else { - final String argType = _className(options.prefix, func.argType); - indent.writeln('$argType *input = [$argType fromMap:message];'); + final String argType = + _objcTypeForDartType(options.prefix, func.argType); + indent.writeln('$argType *input = message;'); syncCall = '[api ${func.name}:input error:&error]'; } if (func.isAsynchronous) { @@ -412,8 +428,7 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) { }); } } else { - const String callback = - 'callback(wrapResult([output toMap], error));'; + const String callback = 'callback(wrapResult(output, error));'; if (func.argType == 'void') { indent.writeScoped( '[api ${func.name}:^($returnType *_Nullable output, FlutterError *_Nullable error) {', @@ -435,7 +450,7 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) { indent.writeln('callback(wrapResult(nil, error));'); } else { indent.writeln('$returnType *output = $syncCall;'); - indent.writeln('callback(wrapResult([output toMap], error));'); + indent.writeln('callback(wrapResult(output, error));'); } } }); @@ -470,7 +485,8 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) { }); indent.addln(''); for (final Method func in api.methods) { - final String returnType = _className(options.prefix, func.returnType); + final String returnType = + _objcTypeForDartType(options.prefix, func.returnType); final String callbackType = _callbackForType(func.returnType, returnType); String sendArgument; @@ -478,10 +494,10 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) { indent.write('- (void)${func.name}:($callbackType)completion '); sendArgument = 'nil'; } else { - final String argType = _className(options.prefix, func.argType); + final String argType = _objcTypeForDartType(options.prefix, func.argType); indent.write( '- (void)${func.name}:($argType*)input completion:($callbackType)completion '); - sendArgument = 'inputMap'; + sendArgument = 'input'; } indent.scoped('{', '}', () { indent.writeln('FlutterBasicMessageChannel *channel ='); @@ -494,17 +510,12 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) { 'codec:${_getCodecGetterName(options.prefix, api.name)}()];'); indent.dec(); indent.dec(); - if (func.argType != 'void') { - indent.writeln('NSDictionary* inputMap = [input toMap];'); - } indent.write('[channel sendMessage:$sendArgument reply:^(id reply) '); indent.scoped('{', '}];', () { if (func.returnType == 'void') { indent.writeln('completion(nil);'); } else { - indent.writeln('NSDictionary* outputMap = reply;'); - indent.writeln( - '$returnType * output = [$returnType fromMap:outputMap];'); + indent.writeln('$returnType * output = reply;'); indent.writeln('completion(output, nil);'); } }); @@ -536,7 +547,7 @@ void generateObjcSource(ObjcOptions options, Root root, StringSink sink) { indent.addln(''); indent.format(''' -static NSDictionary* wrapResult(NSDictionary *result, FlutterError *error) { +static NSDictionary* wrapResult(id result, FlutterError *error) { \tNSDictionary *errorDict = (NSDictionary *)[NSNull null]; \tif (error) { \t\terrorDict = @{ diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index f04ae1dd3144..a1252226f29b 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -25,19 +25,6 @@ import 'ast.dart'; import 'dart_generator.dart'; import 'objc_generator.dart'; -const List _validTypes = [ - 'String', - 'bool', - 'int', - 'double', - 'Uint8List', - 'Int32List', - 'Int64List', - 'Float64List', - 'List', - 'Map', -]; - class _Asynchronous { const _Asynchronous(); } @@ -397,7 +384,7 @@ List _validateAst(Root root, String source) { 'Unsupported datatype:"${field.dataType}" in class "${klass.name}". Generic fields aren\'t yet supported (https://github.com/flutter/flutter/issues/63468).', lineNumber: _calculateLineNumberNullable(source, field.offset), )); - } else if (!(_validTypes.contains(field.dataType) || + } else if (!(validTypes.contains(field.dataType) || customClasses.contains(field.dataType) || customEnums.contains(field.dataType))) { result.add(Error( @@ -413,28 +400,14 @@ List _validateAst(Root root, String source) { if (method.isReturnNullable) { result.add(Error( message: - 'Nullable return types types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}', + 'Nullable return types types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}"', lineNumber: _calculateLineNumberNullable(source, method.offset), )); } if (method.isArgNullable) { result.add(Error( message: - 'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}', - lineNumber: _calculateLineNumberNullable(source, method.offset), - )); - } - if (_validTypes.contains(method.argType)) { - result.add(Error( - message: - 'Primitive argument types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.argType}" in API: "${api.name}" method: "${method.name}', - lineNumber: _calculateLineNumberNullable(source, method.offset), - )); - } - if (_validTypes.contains(method.returnType)) { - result.add(Error( - message: - 'Primitive return types aren\'t yet supported (https://github.com/flutter/flutter/issues/66467): "${method.returnType}" in API: "${api.name}" method: "${method.name}', + 'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" in API: "${api.name}" method: "${method.name}"', lineNumber: _calculateLineNumberNullable(source, method.offset), )); } @@ -502,7 +475,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { orElse: () => Class(name: '', fields: [])); for (final Field field in aClass.fields) { if (!referencedTypes.contains(field.dataType) && - !_validTypes.contains(field.dataType)) { + !validTypes.contains(field.dataType)) { referencedTypes.add(field.dataType); classesToCheck.add(field.dataType); } diff --git a/packages/pigeon/pigeons/flutter_unittests.dart b/packages/pigeon/pigeons/flutter_unittests.dart index cb99095c33ff..de66fed79f91 100644 --- a/packages/pigeon/pigeons/flutter_unittests.dart +++ b/packages/pigeon/pigeons/flutter_unittests.dart @@ -28,4 +28,5 @@ abstract class Api { SearchReply search(SearchRequest request); SearchReplies doSearches(SearchRequests request); SearchRequests echo(SearchRequests requests); + int anInt(int value); } diff --git a/packages/pigeon/pigeons/primitive.dart b/packages/pigeon/pigeons/primitive.dart new file mode 100644 index 000000000000..2b453532b084 --- /dev/null +++ b/packages/pigeon/pigeons/primitive.dart @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. 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:pigeon/pigeon.dart'; + +@HostApi() +abstract class PrimitiveHostApi { + int anInt(int value); + bool aBool(bool value); + String aString(String value); + double aDouble(double value); + // ignore: always_specify_types + Map aMap(Map value); + // ignore: always_specify_types + List aList(List value); + Int32List anInt32List(Int32List value); +} + +@FlutterApi() +abstract class PrimitiveFlutterApi { + int anInt(int value); + bool aBool(bool value); + String aString(String value); + double aDouble(double value); + // ignore: always_specify_types + Map aMap(Map value); + // ignore: always_specify_types + List aList(List value); + Int32List anInt32List(Int32List value); +} diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/.project b/packages/pigeon/platform_tests/android_unit_tests/android/.project new file mode 100644 index 000000000000..fe67538a518f --- /dev/null +++ b/packages/pigeon/platform_tests/android_unit_tests/android/.project @@ -0,0 +1,28 @@ + + + android_ + Project android_ created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1624921942866 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/.settings/org.eclipse.buildship.core.prefs b/packages/pigeon/platform_tests/android_unit_tests/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 000000000000..51db0abebef0 --- /dev/null +++ b/packages/pigeon/platform_tests/android_unit_tests/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments= +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/Library/Java/JavaVirtualMachines/jdk-11-latest/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java index 24b5724b2604..196871dc9c4b 100644 --- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java +++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java @@ -9,15 +9,12 @@ import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; -import io.flutter.plugin.common.StandardMessageCodec; import java.nio.ByteBuffer; import java.util.Map; import org.junit.Test; import org.mockito.ArgumentCaptor; public class PigeonTest { - private MessageCodec codec = StandardMessageCodec.INSTANCE; - @Test public void toMapAndBack() { Pigeon.SetRequest request = new Pigeon.SetRequest(); @@ -62,6 +59,7 @@ public void errorMessage() { ArgumentCaptor handler = ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class); verify(binaryMessenger).setMessageHandler(anyString(), handler.capture()); + MessageCodec codec = Pigeon.Api.getCodec(); ByteBuffer message = codec.encodeMessage(null); handler .getValue() @@ -86,7 +84,8 @@ public void callsVoidMethod() { Pigeon.SetRequest request = new Pigeon.SetRequest(); request.setValue(1234l); request.setState(Pigeon.LoadingState.complete); - ByteBuffer message = codec.encodeMessage(request.toMap()); + MessageCodec codec = Pigeon.Api.getCodec(); + ByteBuffer message = codec.encodeMessage(request); message.rewind(); handler .getValue() diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java new file mode 100644 index 000000000000..36ce51cd35a0 --- /dev/null +++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PrimitiveTest.java @@ -0,0 +1,117 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.example.android_unit_tests; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.example.android_unit_tests.Primitive.PrimitiveFlutterApi; +import io.flutter.plugin.common.BinaryMessenger; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class PrimitiveTest { + private static BinaryMessenger makeMockBinaryMessenger() { + BinaryMessenger binaryMessenger = mock(BinaryMessenger.class); + doAnswer( + invocation -> { + ByteBuffer message = invocation.getArgument(1); + BinaryMessenger.BinaryReply reply = invocation.getArgument(2); + message.position(0); + reply.reply(message); + return null; + }) + .when(binaryMessenger) + .send(anyString(), any(), any()); + return binaryMessenger; + } + + @Test + public void primitiveInt() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.anInt( + 1, + (Integer result) -> { + didCall[0] = true; + assertEquals(result, (Integer) 1); + }); + assertTrue(didCall[0]); + } + + @Test + public void primitiveBool() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.aBool( + true, + (Boolean result) -> { + didCall[0] = true; + assertEquals(result, (Boolean) true); + }); + assertTrue(didCall[0]); + } + + @Test + public void primitiveString() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.aString( + "hello", + (String result) -> { + didCall[0] = true; + assertEquals(result, "hello"); + }); + assertTrue(didCall[0]); + } + + @Test + public void primitiveDouble() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.aDouble( + 1.5, + (Double result) -> { + didCall[0] = true; + assertEquals(result, 1.5, 0.01); + }); + assertTrue(didCall[0]); + } + + @Test + public void primitiveMap() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.aMap( + Collections.singletonMap("hello", 1), + (Map result) -> { + didCall[0] = true; + assertEquals(result, Collections.singletonMap("hello", 1)); + }); + assertTrue(didCall[0]); + } + + @Test + public void primitiveList() { + BinaryMessenger binaryMessenger = makeMockBinaryMessenger(); + PrimitiveFlutterApi api = new PrimitiveFlutterApi(binaryMessenger); + boolean[] didCall = {false}; + api.aList( + Collections.singletonList("hello"), + (List result) -> { + didCall[0] = true; + assertEquals(result, Collections.singletonList("hello")); + }); + assertTrue(didCall[0]); + } +} diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart index 5ad97e5a78b8..09db4570911d 100644 --- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart @@ -60,7 +60,7 @@ class _HostEverythingCodec extends StandardMessageCodec { @override void writeValue(WriteBuffer buffer, Object? value) { if (value is Everything) { - buffer.putUint8(255); + buffer.putUint8(128); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -70,7 +70,7 @@ class _HostEverythingCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 255: + case 128: return Everything.decode(readValue(buffer)!); default: @@ -111,17 +111,16 @@ class HostEverything { details: error['details'], ); } else { - return Everything.decode(replyMap['result']!); + return (replyMap['result'] as Everything?)!; } } Future echo(Everything arg) async { - final Object encoded = arg.encode(); final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.HostEverything.echo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send(encoded) as Map?; + await channel.send(arg) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -137,7 +136,7 @@ class HostEverything { details: error['details'], ); } else { - return Everything.decode(replyMap['result']!); + return (replyMap['result'] as Everything?)!; } } } @@ -147,7 +146,7 @@ class _FlutterEverythingCodec extends StandardMessageCodec { @override void writeValue(WriteBuffer buffer, Object? value) { if (value is Everything) { - buffer.putUint8(255); + buffer.putUint8(128); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -157,7 +156,7 @@ class _FlutterEverythingCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 255: + case 128: return Everything.decode(readValue(buffer)!); default: @@ -181,7 +180,7 @@ abstract class FlutterEverything { channel.setMessageHandler((Object? message) async { // ignore message final Everything output = api.giveMeEverything(); - return output.encode(); + return output; }); } } @@ -194,9 +193,9 @@ abstract class FlutterEverything { channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.FlutterEverything.echo was null. Expected Everything.'); - final Everything input = Everything.decode(message!); + final Everything input = (message as Everything?)!; final Everything output = api.echo(input); - return output.encode(); + return output; }); } } diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart index 5445ab22559e..044bd03e80ee 100644 --- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart @@ -81,16 +81,16 @@ class _ApiCodec extends StandardMessageCodec { @override void writeValue(WriteBuffer buffer, Object? value) { if (value is SearchReplies) { - buffer.putUint8(255); + buffer.putUint8(128); writeValue(buffer, value.encode()); } else if (value is SearchReply) { - buffer.putUint8(254); + buffer.putUint8(129); writeValue(buffer, value.encode()); } else if (value is SearchRequest) { - buffer.putUint8(253); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else if (value is SearchRequests) { - buffer.putUint8(252); + buffer.putUint8(131); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -100,16 +100,16 @@ class _ApiCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 255: + case 128: return SearchReplies.decode(readValue(buffer)!); - case 254: + case 129: return SearchReply.decode(readValue(buffer)!); - case 253: + case 130: return SearchRequest.decode(readValue(buffer)!); - case 252: + case 131: return SearchRequests.decode(readValue(buffer)!); default: @@ -129,12 +129,11 @@ class Api { static const MessageCodec codec = _ApiCodec(); Future search(SearchRequest arg) async { - final Object encoded = arg.encode(); final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.Api.search', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send(encoded) as Map?; + await channel.send(arg) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -150,17 +149,16 @@ class Api { details: error['details'], ); } else { - return SearchReply.decode(replyMap['result']!); + return (replyMap['result'] as SearchReply?)!; } } Future doSearches(SearchRequests arg) async { - final Object encoded = arg.encode(); final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.Api.doSearches', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send(encoded) as Map?; + await channel.send(arg) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -176,17 +174,16 @@ class Api { details: error['details'], ); } else { - return SearchReplies.decode(replyMap['result']!); + return (replyMap['result'] as SearchReplies?)!; } } Future echo(SearchRequests arg) async { - final Object encoded = arg.encode(); final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.Api.echo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send(encoded) as Map?; + await channel.send(arg) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -202,7 +199,32 @@ class Api { details: error['details'], ); } else { - return SearchRequests.decode(replyMap['result']!); + return (replyMap['result'] as SearchRequests?)!; + } + } + + Future anInt(int arg) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.Api.anInt', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(arg) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as int?)!; } } } diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart index b886c77753f8..7061fb8fa47a 100644 --- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart @@ -18,9 +18,9 @@ void main() { final BinaryMessenger mockMessenger = MockBinaryMessenger(); when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any)) .thenAnswer((Invocation realInvocation) async { - const StandardMessageCodec codec = StandardMessageCodec(); + const MessageCodec codec = HostEverything.codec; final Object input = - codec.decodeMessage(realInvocation.positionalArguments[1]); + codec.decodeMessage(realInvocation.positionalArguments[1])!; return codec.encodeMessage({'result': input}); }); final HostEverything api = HostEverything(binaryMessenger: mockMessenger); @@ -53,7 +53,7 @@ void main() { final BinaryMessenger mockMessenger = MockBinaryMessenger(); when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any)) .thenAnswer((Invocation realInvocation) async { - final MessageCodec codec = HostEverything.codec; + const MessageCodec codec = HostEverything.codec; final Object? input = codec.decodeMessage(realInvocation.positionalArguments[1]); return codec.encodeMessage({'result': input!}); diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart index 769625c3d523..08809d56245b 100644 --- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart @@ -38,8 +38,8 @@ void main() { final SearchReply reply = SearchReply()..result = 'ho'; final BinaryMessenger mockMessenger = MockBinaryMessenger(); final Completer completer = Completer(); - completer.complete( - Api.codec.encodeMessage({'result': reply.encode()})); + completer + .complete(Api.codec.encodeMessage({'result': reply})); final Future sendResult = completer.future; when(mockMessenger.send('dev.flutter.pigeon.Api.search', any)) .thenAnswer((Invocation realInvocation) => sendResult); @@ -66,4 +66,19 @@ void main() { expect(echo.requests!.length, 1); expect((echo.requests![0] as SearchRequest?)!.query, 'hey'); }); + + test('primiative datatypes', () async { + final BinaryMessenger mockMessenger = MockBinaryMessenger(); + when(mockMessenger.send('dev.flutter.pigeon.Api.anInt', any)) + .thenAnswer((Invocation realInvocation) async { + final MessageCodec codec = Api.codec; + final Object? input = + codec.decodeMessage(realInvocation.positionalArguments[1]); + final int result = (input as int?)! + 1; + return codec.encodeMessage({'result': result}); + }); + final Api api = Api(binaryMessenger: mockMessenger); + final int result = await api.anInt(1); + expect(result, 2); + }); } diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/Flutter/AppFrameworkInfo.plist b/packages/pigeon/platform_tests/ios_unit_tests/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78a785..f2872cf474ee 100644 --- a/packages/pigeon/platform_tests/ios_unit_tests/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj index 1e359150faa8..43466c9f1589 100644 --- a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 0D50127523FF75B100CD5B95 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D50127423FF75B100CD5B95 /* RunnerTests.m */; }; + 0D6FD3C526A76D400046D8BD /* primitive.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FD3C426A76D400046D8BD /* primitive.gen.m */; }; + 0D6FD3C726A777C00046D8BD /* PrimitiveTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */; }; 0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A9109268D4A050056B5E1 /* ListTest.m */; }; 0D7A910D268E5D700056B5E1 /* all_void.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A910C268E5D700056B5E1 /* all_void.gen.m */; }; 0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D8C35EA25D45A7900B76435 /* AsyncHandlersTest.m */; }; @@ -60,6 +62,9 @@ 0D50127223FF75B100CD5B95 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0D50127423FF75B100CD5B95 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; 0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0D6FD3C326A76D400046D8BD /* primitive.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = primitive.gen.h; sourceTree = ""; }; + 0D6FD3C426A76D400046D8BD /* primitive.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = primitive.gen.m; sourceTree = ""; }; + 0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrimitiveTest.m; sourceTree = ""; }; 0D7A9109268D4A050056B5E1 /* ListTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ListTest.m; sourceTree = ""; }; 0D7A910B268E5D700056B5E1 /* all_void.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = all_void.gen.h; sourceTree = ""; }; 0D7A910C268E5D700056B5E1 /* all_void.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = all_void.gen.m; sourceTree = ""; }; @@ -133,6 +138,7 @@ 0DF4E5C7266ED80900AEA855 /* EchoMessenger.m */, 0DF4E5CA266FDAE300AEA855 /* EnumTest.m */, 0D7A9109268D4A050056B5E1 /* ListTest.m */, + 0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */, ); path = RunnerTests; sourceTree = ""; @@ -170,6 +176,8 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 0D6FD3C326A76D400046D8BD /* primitive.gen.h */, + 0D6FD3C426A76D400046D8BD /* primitive.gen.m */, 0D7A910B268E5D700056B5E1 /* all_void.gen.h */, 0D7A910C268E5D700056B5E1 /* all_void.gen.m */, 0DD2E6AA2684031300A7D764 /* all_datatypes.gen.h */, @@ -354,6 +362,7 @@ 0DF4E5C8266ED80900AEA855 /* EchoMessenger.m in Sources */, 0DF4E5CB266FDAE300AEA855 /* EnumTest.m in Sources */, 0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */, + 0D6FD3C726A777C00046D8BD /* PrimitiveTest.m in Sources */, 0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -370,6 +379,7 @@ 0DD2E6C32684031300A7D764 /* void_arg_flutter.gen.m in Sources */, 0DD2E6C22684031300A7D764 /* voidflutter.gen.m in Sources */, 0DD2E6C02684031300A7D764 /* all_datatypes.gen.m in Sources */, + 0D6FD3C526A76D400046D8BD /* primitive.gen.m in Sources */, 97C146F31CF9000F007C117D /* main.m in Sources */, 0DD2E6BC2684031300A7D764 /* host2flutter.gen.m in Sources */, 0DD2E6BE2684031300A7D764 /* message.gen.m in Sources */, @@ -517,7 +527,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -596,7 +606,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -645,7 +655,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/AsyncHandlersTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/AsyncHandlersTest.m index 88214ae9f309..83ecd790a15f 100644 --- a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/AsyncHandlersTest.m +++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/AsyncHandlersTest.m @@ -8,24 +8,25 @@ /////////////////////////////////////////////////////////////////////////////////////////// @interface Value () -+ (Value*)fromMap:(NSDictionary*)dict; -- (NSDictionary*)toMap; ++ (Value *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; @end /////////////////////////////////////////////////////////////////////////////////////////// @interface MockBinaryMessenger : NSObject -@property(nonatomic, copy) NSNumber* result; -@property(nonatomic, retain) FlutterStandardMessageCodec* codec; -@property(nonatomic, retain) NSMutableDictionary* handlers; +@property(nonatomic, copy) NSNumber *result; +@property(nonatomic, retain) NSObject *codec; +@property(nonatomic, retain) NSMutableDictionary *handlers; +- (instancetype)init NS_UNAVAILABLE; @end /////////////////////////////////////////////////////////////////////////////////////////// @implementation MockBinaryMessenger -- (instancetype)init { +- (instancetype)initWithCodec:(NSObject *)codec { self = [super init]; if (self) { - _codec = [FlutterStandardMessageCodec sharedInstance]; + _codec = codec; _handlers = [[NSMutableDictionary alloc] init]; } return self; @@ -34,21 +35,20 @@ - (instancetype)init { - (void)cleanupConnection:(FlutterBinaryMessengerConnection)connection { } -- (void)sendOnChannel:(nonnull NSString*)channel message:(NSData* _Nullable)message { +- (void)sendOnChannel:(nonnull NSString *)channel message:(NSData *_Nullable)message { } -- (void)sendOnChannel:(nonnull NSString*)channel - message:(NSData* _Nullable)message +- (void)sendOnChannel:(nonnull NSString *)channel + message:(NSData *_Nullable)message binaryReply:(FlutterBinaryReply _Nullable)callback { if (self.result) { - Value* output = [[Value alloc] init]; + Value *output = [[Value alloc] init]; output.number = self.result; - NSDictionary* outputDictionary = [output toMap]; - callback([_codec encode:outputDictionary]); + callback([_codec encode:output]); } } -- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel +- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel binaryMessageHandler: (FlutterBinaryMessageHandler _Nullable)handler { _handlers[channel] = [handler copy]; @@ -59,17 +59,17 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString /////////////////////////////////////////////////////////////////////////////////////////// @interface MockApi2Host : NSObject -@property(nonatomic, copy) NSNumber* output; -@property(nonatomic, retain) FlutterError* voidVoidError; +@property(nonatomic, copy) NSNumber *output; +@property(nonatomic, retain) FlutterError *voidVoidError; @end /////////////////////////////////////////////////////////////////////////////////////////// @implementation MockApi2Host -- (void)calculate:(Value* _Nullable)input - completion:(nonnull void (^)(Value* _Nullable, FlutterError* _Nullable))completion { +- (void)calculate:(Value *_Nullable)input + completion:(nonnull void (^)(Value *_Nullable, FlutterError *_Nullable))completion { if (self.output) { - Value* output = [[Value alloc] init]; + Value *output = [[Value alloc] init]; output.number = self.output; completion(output, nil); } else { @@ -77,7 +77,7 @@ - (void)calculate:(Value* _Nullable)input } } -- (void)voidVoid:(nonnull void (^)(FlutterError* _Nullable))completion { +- (void)voidVoid:(nonnull void (^)(FlutterError *_Nullable))completion { completion(self.voidVoidError); } @@ -91,14 +91,15 @@ @interface AsyncHandlersTest : XCTestCase @implementation AsyncHandlersTest - (void)testAsyncHost2Flutter { - MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; + MockBinaryMessenger *binaryMessenger = + [[MockBinaryMessenger alloc] initWithCodec:Api2FlutterGetCodec()]; binaryMessenger.result = @(2); - Api2Flutter* api2Flutter = [[Api2Flutter alloc] initWithBinaryMessenger:binaryMessenger]; - Value* input = [[Value alloc] init]; + Api2Flutter *api2Flutter = [[Api2Flutter alloc] initWithBinaryMessenger:binaryMessenger]; + Value *input = [[Value alloc] init]; input.number = @(1); - XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"]; [api2Flutter calculate:input - completion:^(Value* _Nonnull output, NSError* _Nullable error) { + completion:^(Value *_Nonnull output, NSError *_Nullable error) { XCTAssertEqual(output.number.intValue, 2); [expectation fulfill]; }]; @@ -106,16 +107,17 @@ - (void)testAsyncHost2Flutter { } - (void)testAsyncFlutter2HostVoidVoid { - MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; - MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; + MockBinaryMessenger *binaryMessenger = + [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()]; + MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init]; mockApi2Host.output = @(2); Api2HostSetup(binaryMessenger, mockApi2Host); - NSString* channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; + NSString *channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); - XCTestExpectation* expectation = [self expectationWithDescription:@"voidvoid callback"]; - binaryMessenger.handlers[channelName](nil, ^(NSData* data) { - NSDictionary* outputMap = [binaryMessenger.codec decode:data]; + XCTestExpectation *expectation = [self expectationWithDescription:@"voidvoid callback"]; + binaryMessenger.handlers[channelName](nil, ^(NSData *data) { + NSDictionary *outputMap = [binaryMessenger.codec decode:data]; XCTAssertEqualObjects(outputMap[@"result"], [NSNull null]); XCTAssertEqualObjects(outputMap[@"error"], [NSNull null]); [expectation fulfill]; @@ -124,16 +126,17 @@ - (void)testAsyncFlutter2HostVoidVoid { } - (void)testAsyncFlutter2HostVoidVoidError { - MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; - MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; + MockBinaryMessenger *binaryMessenger = + [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()]; + MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init]; mockApi2Host.voidVoidError = [FlutterError errorWithCode:@"code" message:@"message" details:nil]; Api2HostSetup(binaryMessenger, mockApi2Host); - NSString* channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; + NSString *channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); - XCTestExpectation* expectation = [self expectationWithDescription:@"voidvoid callback"]; - binaryMessenger.handlers[channelName](nil, ^(NSData* data) { - NSDictionary* outputMap = [binaryMessenger.codec decode:data]; + XCTestExpectation *expectation = [self expectationWithDescription:@"voidvoid callback"]; + binaryMessenger.handlers[channelName](nil, ^(NSData *data) { + NSDictionary *outputMap = [binaryMessenger.codec decode:data]; XCTAssertNotNil(outputMap[@"error"]); XCTAssertEqualObjects(outputMap[@"error"][@"code"], mockApi2Host.voidVoidError.code); [expectation fulfill]; @@ -142,20 +145,21 @@ - (void)testAsyncFlutter2HostVoidVoidError { } - (void)testAsyncFlutter2Host { - MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; - MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; + MockBinaryMessenger *binaryMessenger = + [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()]; + MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init]; mockApi2Host.output = @(2); Api2HostSetup(binaryMessenger, mockApi2Host); - NSString* channelName = @"dev.flutter.pigeon.Api2Host.calculate"; + NSString *channelName = @"dev.flutter.pigeon.Api2Host.calculate"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); - Value* input = [[Value alloc] init]; + Value *input = [[Value alloc] init]; input.number = @(1); - NSData* inputEncoded = [binaryMessenger.codec encode:[input toMap]]; - XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; - binaryMessenger.handlers[channelName](inputEncoded, ^(NSData* data) { - NSDictionary* outputMap = [binaryMessenger.codec decode:data]; - Value* output = [Value fromMap:outputMap[@"result"]]; + NSData *inputEncoded = [binaryMessenger.codec encode:input]; + XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"]; + binaryMessenger.handlers[channelName](inputEncoded, ^(NSData *data) { + NSDictionary *outputMap = [binaryMessenger.codec decode:data]; + Value *output = outputMap[@"result"]; XCTAssertEqual(output.number.intValue, 2); [expectation fulfill]; }); @@ -163,18 +167,19 @@ - (void)testAsyncFlutter2Host { } - (void)testAsyncFlutter2HostError { - MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; - MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; + MockBinaryMessenger *binaryMessenger = + [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()]; + MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init]; Api2HostSetup(binaryMessenger, mockApi2Host); - NSString* channelName = @"dev.flutter.pigeon.Api2Host.calculate"; + NSString *channelName = @"dev.flutter.pigeon.Api2Host.calculate"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); - Value* input = [[Value alloc] init]; + Value *input = [[Value alloc] init]; input.number = @(1); - NSData* inputEncoded = [binaryMessenger.codec encode:[input toMap]]; - XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; - binaryMessenger.handlers[channelName](inputEncoded, ^(NSData* data) { - NSDictionary* outputMap = [binaryMessenger.codec decode:data]; + NSData *inputEncoded = [binaryMessenger.codec encode:[input toMap]]; + XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"]; + binaryMessenger.handlers[channelName](inputEncoded, ^(NSData *data) { + NSDictionary *outputMap = [binaryMessenger.codec decode:data]; XCTAssertNotNil(outputMap[@"error"]); [expectation fulfill]; }); diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/PrimitiveTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/PrimitiveTest.m new file mode 100644 index 000000000000..26fb77c10aa7 --- /dev/null +++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/PrimitiveTest.m @@ -0,0 +1,94 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#import "EchoMessenger.h" +#import "primitive.gen.h" + +/////////////////////////////////////////////////////////////////////////////////////////// +@interface PrimitiveTest : XCTestCase +@end + +/////////////////////////////////////////////////////////////////////////////////////////// +@implementation PrimitiveTest + +- (void)testIntPrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + [api anInt:@1 + completion:^(NSNumber* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(@1, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testBoolPrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + NSNumber* arg = @YES; + [api aBool:arg + completion:^(NSNumber* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(arg, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testDoublePrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + NSNumber* arg = @(1.5); + [api aBool:arg + completion:^(NSNumber* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(arg, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testStringPrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + NSString* arg = @"hello"; + [api aString:arg + completion:^(NSString* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(arg, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testListPrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + NSArray* arg = @[ @"hello" ]; + [api aList:arg + completion:^(NSArray* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(arg, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testMapPrimitive { + EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init]; + PrimitiveFlutterApi* api = [[PrimitiveFlutterApi alloc] initWithBinaryMessenger:binaryMessenger]; + XCTestExpectation* expectation = [self expectationWithDescription:@"callback"]; + NSDictionary* arg = @{ @"hello" : @1 }; + [api aMap:arg + completion:^(NSDictionary* _Nonnull result, NSError* _Nullable err) { + XCTAssertEqualObjects(arg, result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +@end diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh index af121e4b8d7b..341967574189 100755 --- a/packages/pigeon/run_tests.sh +++ b/packages/pigeon/run_tests.sh @@ -249,6 +249,7 @@ run_ios_unittests() { gen_ios_unittests_code ./pigeons/host2flutter.dart "" gen_ios_unittests_code ./pigeons/list.dart "LST" gen_ios_unittests_code ./pigeons/message.dart "" + gen_ios_unittests_code ./pigeons/primitive.dart "" gen_ios_unittests_code ./pigeons/void_arg_flutter.dart "VAF" gen_ios_unittests_code ./pigeons/void_arg_host.dart "VAH" gen_ios_unittests_code ./pigeons/voidflutter.dart "VF" @@ -302,6 +303,7 @@ run_android_unittests() { gen_android_unittests_code ./pigeons/java_double_host_api.dart JavaDoubleHostApi gen_android_unittests_code ./pigeons/list.dart PigeonList gen_android_unittests_code ./pigeons/message.dart MessagePigeon + gen_android_unittests_code ./pigeons/primitive.dart Primitive gen_android_unittests_code ./pigeons/void_arg_flutter.dart VoidArgFlutter gen_android_unittests_code ./pigeons/void_arg_host.dart VoidArgHost gen_android_unittests_code ./pigeons/voidflutter.dart VoidFlutter diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index c26e85d13dc0..04b3488b303d 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -392,7 +392,7 @@ void main() { expect(mainCode, contains('class Api {')); expect(mainCode, isNot(contains('abstract class ApiMock'))); expect(mainCode, isNot(contains('.ApiMock.doSomething'))); - expect(mainCode, isNot(contains('\'${Keys.result}\': output.encode()'))); + expect(mainCode, isNot(contains('\'${Keys.result}\': output'))); expect(mainCode, isNot(contains('return {};'))); generateTestDart( const DartOptions(isNullSafe: false), root, testCodeSink, "fo'o.dart"); @@ -401,7 +401,7 @@ void main() { expect(testCode, isNot(contains('class Api {'))); expect(testCode, contains('abstract class ApiMock')); expect(testCode, isNot(contains('.ApiMock.doSomething'))); - expect(testCode, contains('\'${Keys.result}\': output.encode()')); + expect(testCode, contains('\'${Keys.result}\': output')); expect(testCode, contains('return {};')); }); diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart index 84276b0490d2..4ac1ab63071b 100644 --- a/packages/pigeon/test/java_generator_test.dart +++ b/packages/pigeon/test/java_generator_test.dart @@ -357,7 +357,7 @@ void main() { expect( code, contains( - 'api.doSomething(input, result -> { wrapped.put("result", result.toMap()); reply.reply(wrapped); });')); + 'api.doSomething(input, result -> { wrapped.put("result", result); reply.reply(wrapped); });')); expect(code, contains('channel.setMessageHandler(null)')); }); diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart index 383e84ebd673..8acf43920cc9 100644 --- a/packages/pigeon/test/objc_generator_test.dart +++ b/packages/pigeon/test/objc_generator_test.dart @@ -409,7 +409,7 @@ void main() { generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink); final String code = sink.toString(); expect(code, contains('ABCInput fromMap')); - expect(code, matches('ABCInput.*=.*ABCInput fromMap')); + expect(code, matches('ABCInput.*=.*message')); expect(code, contains('void ABCApiSetup(')); }); diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index c716b8a0b0f7..6aba0a002f30 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -307,28 +307,6 @@ abstract class NestorApi { expect(classNames.contains('OnlyVisibleFromNesting'), true); }); - test('invalid datatype for argument', () { - const String code = ''' -@HostApi() -abstract class InvalidArgTypeApi { - void doit(bool value); -} -'''; - final ParseResults results = _parseSource(code); - expect(results.errors.length, 1); - }); - - test('invalid datatype for argument', () { - const String code = ''' -@HostApi() -abstract class InvalidReturnTypeApi { - bool doit(); -} -'''; - final ParseResults results = _parseSource(code); - expect(results.errors.length, 1); - }); - test('null safety flag', () { final PigeonOptions results = Pigeon.parseArgs(['--dart_null_safety']); @@ -546,32 +524,6 @@ abstract class Api { expect(results.errors[0].message, contains('Nullable')); }); - test('primitive arguments', () { - const String code = ''' -@HostApi() -abstract class Api { - void doit(int foo); -} -'''; - final ParseResults results = _parseSource(code); - expect(results.errors.length, 1); - expect(results.errors[0].lineNumber, 3); - expect(results.errors[0].message, contains('Primitive')); - }); - - test('primitive return', () { - const String code = ''' -@HostApi() -abstract class Api { - int doit(); -} -'''; - final ParseResults results = _parseSource(code); - expect(results.errors.length, 1); - expect(results.errors[0].lineNumber, 3); - expect(results.errors[0].message, contains('Primitive')); - }); - test('test invalid import', () { const String code = 'import \'foo.dart\';\n'; final ParseResults results = _parseSource(code);