[pigeon] implemented primitive datatypes (#414)

This commit is contained in:
gaaclarke
2021-07-27 14:02:04 -07:00
committed by GitHub
parent c359efa333
commit 73c64d8dec
26 changed files with 556 additions and 244 deletions

View File

@ -4,6 +4,7 @@
* [front-end] Added a more explicit error for static fields. * [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). * [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. * 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 ## 0.3.0

View File

@ -121,19 +121,14 @@ final BinaryMessenger$nullTag _binaryMessenger;
} }
String argSignature = ''; String argSignature = '';
String sendArgument = 'null'; String sendArgument = 'null';
String? encodedDeclaration;
if (func.argType != 'void') { if (func.argType != 'void') {
argSignature = '${func.argType} arg'; argSignature = '${func.argType} arg';
sendArgument = 'encoded'; sendArgument = 'arg';
encodedDeclaration = 'final Object encoded = arg.encode();';
} }
indent.write( indent.write(
'Future<${func.returnType}> ${func.name}($argSignature) async ', 'Future<${func.returnType}> ${func.name}($argSignature) async ',
); );
indent.scoped('{', '}', () { indent.scoped('{', '}', () {
if (encodedDeclaration != null) {
indent.writeln(encodedDeclaration);
}
final String channelName = makeChannelName(api, func); final String channelName = makeChannelName(api, func);
indent.writeln( indent.writeln(
'final BasicMessageChannel<Object$nullTag> channel = BasicMessageChannel<Object$nullTag>('); 'final BasicMessageChannel<Object$nullTag> channel = BasicMessageChannel<Object$nullTag>(');
@ -144,7 +139,7 @@ final BinaryMessenger$nullTag _binaryMessenger;
}); });
final String returnStatement = func.returnType == 'void' final String returnStatement = func.returnType == 'void'
? '// noop' ? '// noop'
: 'return ${func.returnType}.decode(replyMap[\'${Keys.result}\']$unwrapOperator);'; : 'return (replyMap[\'${Keys.result}\'] as ${func.returnType}$nullTag)$unwrapOperator;';
indent.format(''' indent.format('''
final Map<Object$nullTag, Object$nullTag>$nullTag replyMap =\n\t\tawait channel.send($sendArgument) as Map<Object$nullTag, Object$nullTag>$nullTag; final Map<Object$nullTag, Object$nullTag>$nullTag replyMap =\n\t\tawait channel.send($sendArgument) as Map<Object$nullTag, Object$nullTag>$nullTag;
if (replyMap == null) { if (replyMap == null) {
@ -237,7 +232,7 @@ void _writeFlutterApi(
'assert(message != null, \'Argument for $channelName was null. Expected $argType.\');', 'assert(message != null, \'Argument for $channelName was null. Expected $argType.\');',
); );
indent.writeln( indent.writeln(
'final $argType input = $argType.decode(message$unwrapOperator);', 'final $argType input = (message as $argType$nullTag)$unwrapOperator;',
); );
call = 'api.${func.name}(input)'; call = 'api.${func.name}(input)';
} }
@ -254,7 +249,7 @@ void _writeFlutterApi(
} else { } else {
indent.writeln('final $returnType output = $call;'); indent.writeln('final $returnType output = $call;');
} }
const String returnExpression = 'output.encode()'; const String returnExpression = 'output';
final String returnStatement = isMockHandler final String returnStatement = isMockHandler
? 'return <Object$nullTag, Object$nullTag>{\'${Keys.result}\': $returnExpression};' ? 'return <Object$nullTag, Object$nullTag>{\'${Keys.result}\': $returnExpression};'
: 'return $returnExpression;'; : 'return $returnExpression;';

View File

@ -262,6 +262,20 @@ class EnumeratedClass {
final int enumeration; final int enumeration;
} }
/// Supported basic datatypes.
const List<String> validTypes = <String>[
'String',
'bool',
'int',
'double',
'Uint8List',
'Int32List',
'Int64List',
'Float64List',
'List',
'Map',
];
/// Custom codecs' custom types are enumerated from 255 down to this number to /// Custom codecs' custom types are enumerated from 255 down to this number to
/// avoid collisions with the StandardMessageCodec. /// avoid collisions with the StandardMessageCodec.
const int _minimumCodecFieldKey = 128; const int _minimumCodecFieldKey = 128;
@ -274,8 +288,10 @@ Iterable<EnumeratedClass> getCodecClasses(Api api) sync* {
names.add(method.returnType); names.add(method.returnType);
names.add(method.argType); names.add(method.argType);
} }
final List<String> sortedNames = final List<String> sortedNames = names
names.where((String element) => element != 'void').toList(); .where((String element) =>
element != 'void' && !validTypes.contains(element))
.toList();
sortedNames.sort(); sortedNames.sort();
int enumeration = _minimumCodecFieldKey; int enumeration = _minimumCodecFieldKey;
const int maxCustomClassesPerApi = 255 - _minimumCodecFieldKey; const int maxCustomClassesPerApi = 255 - _minimumCodecFieldKey;

View File

@ -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<String, String> map = <String, String>{
'int': 'Integer',
'bool': 'Boolean',
'double': 'Double',
'Int32List': 'int[]',
'Uint8List': 'byte[]',
'Int64List': 'long[]',
'Float64List': 'double[]',
};
return map[type] ?? type;
}
void _writeHostApi(Indent indent, Api api) { void _writeHostApi(Indent indent, Api api) {
assert(api.location == ApiLocation.host); assert(api.location == ApiLocation.host);
@ -121,11 +137,13 @@ void _writeHostApi(Indent indent, Api api) {
indent.write('public interface ${api.name} '); indent.write('public interface ${api.name} ');
indent.scoped('{', '}', () { indent.scoped('{', '}', () {
for (final Method method in api.methods) { for (final Method method in api.methods) {
final String returnType = final String argType = _javaTypeForDartTypePassthrough(method.argType);
method.isAsynchronous ? 'void' : method.returnType; final String returnType = method.isAsynchronous
? 'void'
: _javaTypeForDartTypePassthrough(method.returnType);
final List<String> argSignature = <String>[]; final List<String> argSignature = <String>[];
if (method.argType != 'void') { if (method.argType != 'void') {
argSignature.add('${method.argType} arg'); argSignature.add('$argType arg');
} }
if (method.isAsynchronous) { if (method.isAsynchronous) {
final String returnType = final String returnType =
@ -162,21 +180,27 @@ static MessageCodec<Object> getCodec() {
indent.scoped('{', '} else {', () { indent.scoped('{', '} else {', () {
indent.write('channel.setMessageHandler((message, reply) -> '); indent.write('channel.setMessageHandler((message, reply) -> ');
indent.scoped('{', '});', () { indent.scoped('{', '});', () {
final String argType = method.argType; final String argType =
final String returnType = method.returnType; _javaTypeForDartTypePassthrough(method.argType);
final String returnType =
_javaTypeForDartTypePassthrough(method.returnType);
indent.writeln('Map<String, Object> wrapped = new HashMap<>();'); indent.writeln('Map<String, Object> wrapped = new HashMap<>();');
indent.write('try '); indent.write('try ');
indent.scoped('{', '}', () { indent.scoped('{', '}', () {
final List<String> methodArgument = <String>[]; final List<String> methodArgument = <String>[];
if (argType != 'void') { if (argType != 'void') {
indent.writeln('@SuppressWarnings("ConstantConditions")'); indent.writeln('@SuppressWarnings("ConstantConditions")');
indent.writeln( indent.writeln('$argType input = ($argType)message;');
'$argType input = $argType.fromMap((Map<String, Object>)message);'); indent.write('if (input == null) ');
indent.scoped('{', '}', () {
indent.writeln(
'throw new NullPointerException("Message unexpectedly null.");');
});
methodArgument.add('input'); methodArgument.add('input');
} }
if (method.isAsynchronous) { if (method.isAsynchronous) {
final String resultValue = final String resultValue =
method.returnType == 'void' ? 'null' : 'result.toMap()'; method.returnType == 'void' ? 'null' : 'result';
methodArgument.add( methodArgument.add(
'result -> { ' 'result -> { '
'wrapped.put("${Keys.result}", $resultValue); ' 'wrapped.put("${Keys.result}", $resultValue); '
@ -193,8 +217,7 @@ static MessageCodec<Object> getCodec() {
indent.writeln('wrapped.put("${Keys.result}", null);'); indent.writeln('wrapped.put("${Keys.result}", null);');
} else { } else {
indent.writeln('$returnType output = $call;'); indent.writeln('$returnType output = $call;');
indent.writeln( indent.writeln('wrapped.put("${Keys.result}", output);');
'wrapped.put("${Keys.result}", output.toMap());');
} }
}); });
indent.write('catch (Error | RuntimeException exception) '); indent.write('catch (Error | RuntimeException exception) ');
@ -242,16 +265,18 @@ static MessageCodec<Object> getCodec() {
'''); ''');
for (final Method func in api.methods) { for (final Method func in api.methods) {
final String channelName = makeChannelName(api, func); final String channelName = makeChannelName(api, func);
final String returnType = final String returnType = func.returnType == 'void'
func.returnType == 'void' ? 'Void' : func.returnType; ? 'Void'
: _javaTypeForDartTypePassthrough(func.returnType);
final String argType = _javaTypeForDartTypePassthrough(func.argType);
String sendArgument; String sendArgument;
if (func.argType == 'void') { if (func.argType == 'void') {
indent.write('public void ${func.name}(Reply<$returnType> callback) '); indent.write('public void ${func.name}(Reply<$returnType> callback) ');
sendArgument = 'null'; sendArgument = 'null';
} else { } else {
indent.write( indent.write(
'public void ${func.name}(${func.argType} argInput, Reply<$returnType> callback) '); 'public void ${func.name}($argType argInput, Reply<$returnType> callback) ');
sendArgument = 'inputMap'; sendArgument = 'argInput';
} }
indent.scoped('{', '}', () { indent.scoped('{', '}', () {
indent.writeln('BasicMessageChannel<Object> channel ='); indent.writeln('BasicMessageChannel<Object> channel =');
@ -261,18 +286,13 @@ static MessageCodec<Object> getCodec() {
'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());'); 'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
indent.dec(); indent.dec();
indent.dec(); indent.dec();
if (func.argType != 'void') {
indent.writeln('Map<String, Object> inputMap = argInput.toMap();');
}
indent.write('channel.send($sendArgument, channelReply -> '); indent.write('channel.send($sendArgument, channelReply -> ');
indent.scoped('{', '});', () { indent.scoped('{', '});', () {
if (func.returnType == 'void') { if (func.returnType == 'void') {
indent.writeln('callback.reply(null);'); indent.writeln('callback.reply(null);');
} else { } else {
indent.writeln('Map outputMap = (Map)channelReply;');
indent.writeln('@SuppressWarnings("ConstantConditions")'); indent.writeln('@SuppressWarnings("ConstantConditions")');
indent.writeln( indent.writeln('$returnType output = ($returnType)channelReply;');
'${func.returnType} output = ${func.returnType}.fromMap(outputMap);');
indent.writeln('callback.reply(output);'); indent.writeln('callback.reply(output);');
} }
}); });

View File

@ -67,16 +67,16 @@ String _callbackForType(String dartType, String objcType) {
} }
const Map<String, String> _objcTypeForDartTypeMap = <String, String>{ const Map<String, String> _objcTypeForDartTypeMap = <String, String>{
'bool': 'NSNumber *', 'bool': 'NSNumber',
'int': 'NSNumber *', 'int': 'NSNumber',
'String': 'NSString *', 'String': 'NSString',
'double': 'NSNumber *', 'double': 'NSNumber',
'Uint8List': 'FlutterStandardTypedData *', 'Uint8List': 'FlutterStandardTypedData',
'Int32List': 'FlutterStandardTypedData *', 'Int32List': 'FlutterStandardTypedData',
'Int64List': 'FlutterStandardTypedData *', 'Int64List': 'FlutterStandardTypedData',
'Float64List': 'FlutterStandardTypedData *', 'Float64List': 'FlutterStandardTypedData',
'List': 'NSArray *', 'List': 'NSArray',
'Map': 'NSDictionary *', 'Map': 'NSDictionary',
}; };
const Map<String, String> _propertyTypeForDartTypeMap = <String, String>{ const Map<String, String> _propertyTypeForDartTypeMap = <String, String>{
@ -92,8 +92,18 @@ const Map<String, String> _propertyTypeForDartTypeMap = <String, String>{
'Map': 'strong', 'Map': 'strong',
}; };
String? _objcTypeForDartType(String type) { String? _objcTypePtrForPrimitiveDartType(String type) {
return _objcTypeForDartTypeMap[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) { String _propertyTypeForDartType(String type) {
@ -112,7 +122,7 @@ void _writeClassDeclarations(
indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject'); indent.writeln('@interface ${_className(prefix, klass.name)} : NSObject');
for (final Field field in klass.fields) { for (final Field field in klass.fields) {
final HostDatatype hostDatatype = getHostDatatype( final HostDatatype hostDatatype = getHostDatatype(
field, classes, enums, _objcTypeForDartType, field, classes, enums, _objcTypePtrForPrimitiveDartType,
customResolver: enumNames.contains(field.dataType) customResolver: enumNames.contains(field.dataType)
? (String x) => _className(prefix, x) ? (String x) => _className(prefix, x)
: (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); final String apiName = _className(options.prefix, api.name);
indent.writeln('@protocol $apiName'); indent.writeln('@protocol $apiName');
for (final Method func in api.methods) { 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.isAsynchronous) {
if (func.returnType == 'void') { if (func.returnType == 'void') {
if (func.argType == 'void') { if (func.argType == 'void') {
indent.writeln( indent.writeln(
'-(void)${func.name}:(void(^)(FlutterError *_Nullable))completion;'); '-(void)${func.name}:(void(^)(FlutterError *_Nullable))completion;');
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType =
_objcTypeForDartType(options.prefix, func.argType);
indent.writeln( indent.writeln(
'-(void)${func.name}:(nullable $argType *)input completion:(void(^)(FlutterError *_Nullable))completion;'); '-(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( indent.writeln(
'-(void)${func.name}:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;'); '-(void)${func.name}:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;');
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType =
_objcTypeForDartType(options.prefix, func.argType);
indent.writeln( indent.writeln(
'-(void)${func.name}:(nullable $argType *)input completion:(void(^)($returnTypeName *_Nullable, FlutterError *_Nullable))completion;'); '-(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( indent.writeln(
'-($returnType)${func.name}:(FlutterError *_Nullable *_Nonnull)error;'); '-($returnType)${func.name}:(FlutterError *_Nullable *_Nonnull)error;');
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType =
_objcTypeForDartType(options.prefix, func.argType);
indent.writeln( indent.writeln(
'-($returnType)${func.name}:($argType*)input error:(FlutterError *_Nullable *_Nonnull)error;'); '-($returnType)${func.name}:($argType*)input error:(FlutterError *_Nullable *_Nonnull)error;');
} }
@ -264,12 +278,13 @@ void _writeFlutterApiDeclaration(Indent indent, Api api, ObjcOptions options) {
indent.writeln( indent.writeln(
'- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;'); '- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;');
for (final Method func in api.methods) { 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); final String callbackType = _callbackForType(func.returnType, returnType);
if (func.argType == 'void') { if (func.argType == 'void') {
indent.writeln('- (void)${func.name}:($callbackType)completion;'); indent.writeln('- (void)${func.name}:($callbackType)completion;');
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType = _objcTypeForDartType(options.prefix, func.argType);
indent.writeln( indent.writeln(
'- (void)${func.name}:($argType*)input completion:($callbackType)completion;'); '- (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) '); '[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) ');
indent.scoped('{', '}];', () { indent.scoped('{', '}];', () {
final String returnType = final String returnType =
_className(options.prefix, func.returnType); _objcTypeForDartType(options.prefix, func.returnType);
String syncCall; String syncCall;
if (func.argType == 'void') { if (func.argType == 'void') {
syncCall = '[api ${func.name}:&error]'; syncCall = '[api ${func.name}:&error]';
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType =
indent.writeln('$argType *input = [$argType fromMap:message];'); _objcTypeForDartType(options.prefix, func.argType);
indent.writeln('$argType *input = message;');
syncCall = '[api ${func.name}:input error:&error]'; syncCall = '[api ${func.name}:input error:&error]';
} }
if (func.isAsynchronous) { if (func.isAsynchronous) {
@ -412,8 +428,7 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
}); });
} }
} else { } else {
const String callback = const String callback = 'callback(wrapResult(output, error));';
'callback(wrapResult([output toMap], error));';
if (func.argType == 'void') { if (func.argType == 'void') {
indent.writeScoped( indent.writeScoped(
'[api ${func.name}:^($returnType *_Nullable output, FlutterError *_Nullable error) {', '[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));'); indent.writeln('callback(wrapResult(nil, error));');
} else { } else {
indent.writeln('$returnType *output = $syncCall;'); 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(''); indent.addln('');
for (final Method func in api.methods) { 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); final String callbackType = _callbackForType(func.returnType, returnType);
String sendArgument; String sendArgument;
@ -478,10 +494,10 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) {
indent.write('- (void)${func.name}:($callbackType)completion '); indent.write('- (void)${func.name}:($callbackType)completion ');
sendArgument = 'nil'; sendArgument = 'nil';
} else { } else {
final String argType = _className(options.prefix, func.argType); final String argType = _objcTypeForDartType(options.prefix, func.argType);
indent.write( indent.write(
'- (void)${func.name}:($argType*)input completion:($callbackType)completion '); '- (void)${func.name}:($argType*)input completion:($callbackType)completion ');
sendArgument = 'inputMap'; sendArgument = 'input';
} }
indent.scoped('{', '}', () { indent.scoped('{', '}', () {
indent.writeln('FlutterBasicMessageChannel *channel ='); indent.writeln('FlutterBasicMessageChannel *channel =');
@ -494,17 +510,12 @@ void _writeFlutterApiSource(Indent indent, ObjcOptions options, Api api) {
'codec:${_getCodecGetterName(options.prefix, api.name)}()];'); 'codec:${_getCodecGetterName(options.prefix, api.name)}()];');
indent.dec(); indent.dec();
indent.dec(); indent.dec();
if (func.argType != 'void') {
indent.writeln('NSDictionary* inputMap = [input toMap];');
}
indent.write('[channel sendMessage:$sendArgument reply:^(id reply) '); indent.write('[channel sendMessage:$sendArgument reply:^(id reply) ');
indent.scoped('{', '}];', () { indent.scoped('{', '}];', () {
if (func.returnType == 'void') { if (func.returnType == 'void') {
indent.writeln('completion(nil);'); indent.writeln('completion(nil);');
} else { } else {
indent.writeln('NSDictionary* outputMap = reply;'); indent.writeln('$returnType * output = reply;');
indent.writeln(
'$returnType * output = [$returnType fromMap:outputMap];');
indent.writeln('completion(output, nil);'); indent.writeln('completion(output, nil);');
} }
}); });
@ -536,7 +547,7 @@ void generateObjcSource(ObjcOptions options, Root root, StringSink sink) {
indent.addln(''); indent.addln('');
indent.format(''' indent.format('''
static NSDictionary<NSString*, id>* wrapResult(NSDictionary *result, FlutterError *error) { static NSDictionary<NSString*, id>* wrapResult(id result, FlutterError *error) {
\tNSDictionary *errorDict = (NSDictionary *)[NSNull null]; \tNSDictionary *errorDict = (NSDictionary *)[NSNull null];
\tif (error) { \tif (error) {
\t\terrorDict = @{ \t\terrorDict = @{

View File

@ -25,19 +25,6 @@ import 'ast.dart';
import 'dart_generator.dart'; import 'dart_generator.dart';
import 'objc_generator.dart'; import 'objc_generator.dart';
const List<String> _validTypes = <String>[
'String',
'bool',
'int',
'double',
'Uint8List',
'Int32List',
'Int64List',
'Float64List',
'List',
'Map',
];
class _Asynchronous { class _Asynchronous {
const _Asynchronous(); const _Asynchronous();
} }
@ -397,7 +384,7 @@ List<Error> _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).', '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), lineNumber: _calculateLineNumberNullable(source, field.offset),
)); ));
} else if (!(_validTypes.contains(field.dataType) || } else if (!(validTypes.contains(field.dataType) ||
customClasses.contains(field.dataType) || customClasses.contains(field.dataType) ||
customEnums.contains(field.dataType))) { customEnums.contains(field.dataType))) {
result.add(Error( result.add(Error(
@ -413,28 +400,14 @@ List<Error> _validateAst(Root root, String source) {
if (method.isReturnNullable) { if (method.isReturnNullable) {
result.add(Error( result.add(Error(
message: 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), lineNumber: _calculateLineNumberNullable(source, method.offset),
)); ));
} }
if (method.isArgNullable) { if (method.isArgNullable) {
result.add(Error( result.add(Error(
message: message:
'Nullable argument types aren\'t supported for Pigeon methods: "${method.argType}" 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),
));
}
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}',
lineNumber: _calculateLineNumberNullable(source, method.offset), lineNumber: _calculateLineNumberNullable(source, method.offset),
)); ));
} }
@ -502,7 +475,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
orElse: () => Class(name: '', fields: <Field>[])); orElse: () => Class(name: '', fields: <Field>[]));
for (final Field field in aClass.fields) { for (final Field field in aClass.fields) {
if (!referencedTypes.contains(field.dataType) && if (!referencedTypes.contains(field.dataType) &&
!_validTypes.contains(field.dataType)) { !validTypes.contains(field.dataType)) {
referencedTypes.add(field.dataType); referencedTypes.add(field.dataType);
classesToCheck.add(field.dataType); classesToCheck.add(field.dataType);
} }

View File

@ -28,4 +28,5 @@ abstract class Api {
SearchReply search(SearchRequest request); SearchReply search(SearchRequest request);
SearchReplies doSearches(SearchRequests request); SearchReplies doSearches(SearchRequests request);
SearchRequests echo(SearchRequests requests); SearchRequests echo(SearchRequests requests);
int anInt(int value);
} }

View File

@ -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);
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>android_</name>
<comment>Project android_ created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1624921942866</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@ -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

View File

@ -9,15 +9,12 @@ import static org.mockito.Mockito.*;
import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MessageCodec; import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map; import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
public class PigeonTest { public class PigeonTest {
private MessageCodec<Object> codec = StandardMessageCodec.INSTANCE;
@Test @Test
public void toMapAndBack() { public void toMapAndBack() {
Pigeon.SetRequest request = new Pigeon.SetRequest(); Pigeon.SetRequest request = new Pigeon.SetRequest();
@ -62,6 +59,7 @@ public class PigeonTest {
ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler = ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler =
ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class); ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
verify(binaryMessenger).setMessageHandler(anyString(), handler.capture()); verify(binaryMessenger).setMessageHandler(anyString(), handler.capture());
MessageCodec<Object> codec = Pigeon.Api.getCodec();
ByteBuffer message = codec.encodeMessage(null); ByteBuffer message = codec.encodeMessage(null);
handler handler
.getValue() .getValue()
@ -86,7 +84,8 @@ public class PigeonTest {
Pigeon.SetRequest request = new Pigeon.SetRequest(); Pigeon.SetRequest request = new Pigeon.SetRequest();
request.setValue(1234l); request.setValue(1234l);
request.setState(Pigeon.LoadingState.complete); request.setState(Pigeon.LoadingState.complete);
ByteBuffer message = codec.encodeMessage(request.toMap()); MessageCodec<Object> codec = Pigeon.Api.getCodec();
ByteBuffer message = codec.encodeMessage(request);
message.rewind(); message.rewind();
handler handler
.getValue() .getValue()

View File

@ -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]);
}
}

View File

@ -60,7 +60,7 @@ class _HostEverythingCodec extends StandardMessageCodec {
@override @override
void writeValue(WriteBuffer buffer, Object? value) { void writeValue(WriteBuffer buffer, Object? value) {
if (value is Everything) { if (value is Everything) {
buffer.putUint8(255); buffer.putUint8(128);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
@ -70,7 +70,7 @@ class _HostEverythingCodec extends StandardMessageCodec {
@override @override
Object? readValueOfType(int type, ReadBuffer buffer) { Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) { switch (type) {
case 255: case 128:
return Everything.decode(readValue(buffer)!); return Everything.decode(readValue(buffer)!);
default: default:
@ -111,17 +111,16 @@ class HostEverything {
details: error['details'], details: error['details'],
); );
} else { } else {
return Everything.decode(replyMap['result']!); return (replyMap['result'] as Everything?)!;
} }
} }
Future<Everything> echo(Everything arg) async { Future<Everything> echo(Everything arg) async {
final Object encoded = arg.encode();
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.HostEverything.echo', codec, 'dev.flutter.pigeon.HostEverything.echo', codec,
binaryMessenger: _binaryMessenger); binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap = final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?; await channel.send(arg) as Map<Object?, Object?>?;
if (replyMap == null) { if (replyMap == null) {
throw PlatformException( throw PlatformException(
code: 'channel-error', code: 'channel-error',
@ -137,7 +136,7 @@ class HostEverything {
details: error['details'], details: error['details'],
); );
} else { } else {
return Everything.decode(replyMap['result']!); return (replyMap['result'] as Everything?)!;
} }
} }
} }
@ -147,7 +146,7 @@ class _FlutterEverythingCodec extends StandardMessageCodec {
@override @override
void writeValue(WriteBuffer buffer, Object? value) { void writeValue(WriteBuffer buffer, Object? value) {
if (value is Everything) { if (value is Everything) {
buffer.putUint8(255); buffer.putUint8(128);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
@ -157,7 +156,7 @@ class _FlutterEverythingCodec extends StandardMessageCodec {
@override @override
Object? readValueOfType(int type, ReadBuffer buffer) { Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) { switch (type) {
case 255: case 128:
return Everything.decode(readValue(buffer)!); return Everything.decode(readValue(buffer)!);
default: default:
@ -181,7 +180,7 @@ abstract class FlutterEverything {
channel.setMessageHandler((Object? message) async { channel.setMessageHandler((Object? message) async {
// ignore message // ignore message
final Everything output = api.giveMeEverything(); final Everything output = api.giveMeEverything();
return output.encode(); return output;
}); });
} }
} }
@ -194,9 +193,9 @@ abstract class FlutterEverything {
channel.setMessageHandler((Object? message) async { channel.setMessageHandler((Object? message) async {
assert(message != null, assert(message != null,
'Argument for dev.flutter.pigeon.FlutterEverything.echo was null. Expected Everything.'); '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); final Everything output = api.echo(input);
return output.encode(); return output;
}); });
} }
} }

View File

@ -81,16 +81,16 @@ class _ApiCodec extends StandardMessageCodec {
@override @override
void writeValue(WriteBuffer buffer, Object? value) { void writeValue(WriteBuffer buffer, Object? value) {
if (value is SearchReplies) { if (value is SearchReplies) {
buffer.putUint8(255); buffer.putUint8(128);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SearchReply) { } else if (value is SearchReply) {
buffer.putUint8(254); buffer.putUint8(129);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SearchRequest) { } else if (value is SearchRequest) {
buffer.putUint8(253); buffer.putUint8(130);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SearchRequests) { } else if (value is SearchRequests) {
buffer.putUint8(252); buffer.putUint8(131);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
@ -100,16 +100,16 @@ class _ApiCodec extends StandardMessageCodec {
@override @override
Object? readValueOfType(int type, ReadBuffer buffer) { Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) { switch (type) {
case 255: case 128:
return SearchReplies.decode(readValue(buffer)!); return SearchReplies.decode(readValue(buffer)!);
case 254: case 129:
return SearchReply.decode(readValue(buffer)!); return SearchReply.decode(readValue(buffer)!);
case 253: case 130:
return SearchRequest.decode(readValue(buffer)!); return SearchRequest.decode(readValue(buffer)!);
case 252: case 131:
return SearchRequests.decode(readValue(buffer)!); return SearchRequests.decode(readValue(buffer)!);
default: default:
@ -129,12 +129,11 @@ class Api {
static const MessageCodec<Object?> codec = _ApiCodec(); static const MessageCodec<Object?> codec = _ApiCodec();
Future<SearchReply> search(SearchRequest arg) async { Future<SearchReply> search(SearchRequest arg) async {
final Object encoded = arg.encode();
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.search', codec, 'dev.flutter.pigeon.Api.search', codec,
binaryMessenger: _binaryMessenger); binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap = final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?; await channel.send(arg) as Map<Object?, Object?>?;
if (replyMap == null) { if (replyMap == null) {
throw PlatformException( throw PlatformException(
code: 'channel-error', code: 'channel-error',
@ -150,17 +149,16 @@ class Api {
details: error['details'], details: error['details'],
); );
} else { } else {
return SearchReply.decode(replyMap['result']!); return (replyMap['result'] as SearchReply?)!;
} }
} }
Future<SearchReplies> doSearches(SearchRequests arg) async { Future<SearchReplies> doSearches(SearchRequests arg) async {
final Object encoded = arg.encode();
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.doSearches', codec, 'dev.flutter.pigeon.Api.doSearches', codec,
binaryMessenger: _binaryMessenger); binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap = final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?; await channel.send(arg) as Map<Object?, Object?>?;
if (replyMap == null) { if (replyMap == null) {
throw PlatformException( throw PlatformException(
code: 'channel-error', code: 'channel-error',
@ -176,17 +174,16 @@ class Api {
details: error['details'], details: error['details'],
); );
} else { } else {
return SearchReplies.decode(replyMap['result']!); return (replyMap['result'] as SearchReplies?)!;
} }
} }
Future<SearchRequests> echo(SearchRequests arg) async { Future<SearchRequests> echo(SearchRequests arg) async {
final Object encoded = arg.encode();
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.echo', codec, 'dev.flutter.pigeon.Api.echo', codec,
binaryMessenger: _binaryMessenger); binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap = final Map<Object?, Object?>? replyMap =
await channel.send(encoded) as Map<Object?, Object?>?; await channel.send(arg) as Map<Object?, Object?>?;
if (replyMap == null) { if (replyMap == null) {
throw PlatformException( throw PlatformException(
code: 'channel-error', code: 'channel-error',
@ -202,7 +199,32 @@ class Api {
details: error['details'], details: error['details'],
); );
} else { } else {
return SearchRequests.decode(replyMap['result']!); return (replyMap['result'] as SearchRequests?)!;
}
}
Future<int> anInt(int arg) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.Api.anInt', codec,
binaryMessenger: _binaryMessenger);
final Map<Object?, Object?>? replyMap =
await channel.send(arg) as Map<Object?, Object?>?;
if (replyMap == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
details: null,
);
} else if (replyMap['error'] != null) {
final Map<Object?, Object?> error =
(replyMap['error'] as Map<Object?, Object?>?)!;
throw PlatformException(
code: (error['code'] as String?)!,
message: error['message'] as String?,
details: error['details'],
);
} else {
return (replyMap['result'] as int?)!;
} }
} }
} }

View File

@ -18,9 +18,9 @@ void main() {
final BinaryMessenger mockMessenger = MockBinaryMessenger(); final BinaryMessenger mockMessenger = MockBinaryMessenger();
when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any)) when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any))
.thenAnswer((Invocation realInvocation) async { .thenAnswer((Invocation realInvocation) async {
const StandardMessageCodec codec = StandardMessageCodec(); const MessageCodec<Object?> codec = HostEverything.codec;
final Object input = final Object input =
codec.decodeMessage(realInvocation.positionalArguments[1]); codec.decodeMessage(realInvocation.positionalArguments[1])!;
return codec.encodeMessage(<String, Object>{'result': input}); return codec.encodeMessage(<String, Object>{'result': input});
}); });
final HostEverything api = HostEverything(binaryMessenger: mockMessenger); final HostEverything api = HostEverything(binaryMessenger: mockMessenger);
@ -53,7 +53,7 @@ void main() {
final BinaryMessenger mockMessenger = MockBinaryMessenger(); final BinaryMessenger mockMessenger = MockBinaryMessenger();
when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any)) when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any))
.thenAnswer((Invocation realInvocation) async { .thenAnswer((Invocation realInvocation) async {
final MessageCodec<Object?> codec = HostEverything.codec; const MessageCodec<Object?> codec = HostEverything.codec;
final Object? input = final Object? input =
codec.decodeMessage(realInvocation.positionalArguments[1]); codec.decodeMessage(realInvocation.positionalArguments[1]);
return codec.encodeMessage(<String, Object>{'result': input!}); return codec.encodeMessage(<String, Object>{'result': input!});

View File

@ -38,8 +38,8 @@ void main() {
final SearchReply reply = SearchReply()..result = 'ho'; final SearchReply reply = SearchReply()..result = 'ho';
final BinaryMessenger mockMessenger = MockBinaryMessenger(); final BinaryMessenger mockMessenger = MockBinaryMessenger();
final Completer<ByteData?> completer = Completer<ByteData?>(); final Completer<ByteData?> completer = Completer<ByteData?>();
completer.complete( completer
Api.codec.encodeMessage(<String, Object>{'result': reply.encode()})); .complete(Api.codec.encodeMessage(<String, Object>{'result': reply}));
final Future<ByteData?> sendResult = completer.future; final Future<ByteData?> sendResult = completer.future;
when(mockMessenger.send('dev.flutter.pigeon.Api.search', any)) when(mockMessenger.send('dev.flutter.pigeon.Api.search', any))
.thenAnswer((Invocation realInvocation) => sendResult); .thenAnswer((Invocation realInvocation) => sendResult);
@ -66,4 +66,19 @@ void main() {
expect(echo.requests!.length, 1); expect(echo.requests!.length, 1);
expect((echo.requests![0] as SearchRequest?)!.query, 'hey'); 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<Object?> codec = Api.codec;
final Object? input =
codec.decodeMessage(realInvocation.positionalArguments[1]);
final int result = (input as int?)! + 1;
return codec.encodeMessage(<String, Object>{'result': result});
});
final Api api = Api(binaryMessenger: mockMessenger);
final int result = await api.anInt(1);
expect(result, 2);
});
} }

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>8.0</string> <string>9.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -8,6 +8,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0D50127523FF75B100CD5B95 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D50127423FF75B100CD5B95 /* RunnerTests.m */; }; 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 */; }; 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 */; }; 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 */; }; 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; }; 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 = "<group>"; }; 0D50127423FF75B100CD5B95 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0D6FD3C326A76D400046D8BD /* primitive.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = primitive.gen.h; sourceTree = "<group>"; };
0D6FD3C426A76D400046D8BD /* primitive.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = primitive.gen.m; sourceTree = "<group>"; };
0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrimitiveTest.m; sourceTree = "<group>"; };
0D7A9109268D4A050056B5E1 /* ListTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ListTest.m; sourceTree = "<group>"; }; 0D7A9109268D4A050056B5E1 /* ListTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ListTest.m; sourceTree = "<group>"; };
0D7A910B268E5D700056B5E1 /* all_void.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = all_void.gen.h; sourceTree = "<group>"; }; 0D7A910B268E5D700056B5E1 /* all_void.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = all_void.gen.h; sourceTree = "<group>"; };
0D7A910C268E5D700056B5E1 /* all_void.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = all_void.gen.m; sourceTree = "<group>"; }; 0D7A910C268E5D700056B5E1 /* all_void.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = all_void.gen.m; sourceTree = "<group>"; };
@ -133,6 +138,7 @@
0DF4E5C7266ED80900AEA855 /* EchoMessenger.m */, 0DF4E5C7266ED80900AEA855 /* EchoMessenger.m */,
0DF4E5CA266FDAE300AEA855 /* EnumTest.m */, 0DF4E5CA266FDAE300AEA855 /* EnumTest.m */,
0D7A9109268D4A050056B5E1 /* ListTest.m */, 0D7A9109268D4A050056B5E1 /* ListTest.m */,
0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */,
); );
path = RunnerTests; path = RunnerTests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -170,6 +176,8 @@
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0D6FD3C326A76D400046D8BD /* primitive.gen.h */,
0D6FD3C426A76D400046D8BD /* primitive.gen.m */,
0D7A910B268E5D700056B5E1 /* all_void.gen.h */, 0D7A910B268E5D700056B5E1 /* all_void.gen.h */,
0D7A910C268E5D700056B5E1 /* all_void.gen.m */, 0D7A910C268E5D700056B5E1 /* all_void.gen.m */,
0DD2E6AA2684031300A7D764 /* all_datatypes.gen.h */, 0DD2E6AA2684031300A7D764 /* all_datatypes.gen.h */,
@ -354,6 +362,7 @@
0DF4E5C8266ED80900AEA855 /* EchoMessenger.m in Sources */, 0DF4E5C8266ED80900AEA855 /* EchoMessenger.m in Sources */,
0DF4E5CB266FDAE300AEA855 /* EnumTest.m in Sources */, 0DF4E5CB266FDAE300AEA855 /* EnumTest.m in Sources */,
0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */, 0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */,
0D6FD3C726A777C00046D8BD /* PrimitiveTest.m in Sources */,
0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */, 0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -370,6 +379,7 @@
0DD2E6C32684031300A7D764 /* void_arg_flutter.gen.m in Sources */, 0DD2E6C32684031300A7D764 /* void_arg_flutter.gen.m in Sources */,
0DD2E6C22684031300A7D764 /* voidflutter.gen.m in Sources */, 0DD2E6C22684031300A7D764 /* voidflutter.gen.m in Sources */,
0DD2E6C02684031300A7D764 /* all_datatypes.gen.m in Sources */, 0DD2E6C02684031300A7D764 /* all_datatypes.gen.m in Sources */,
0D6FD3C526A76D400046D8BD /* primitive.gen.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */, 97C146F31CF9000F007C117D /* main.m in Sources */,
0DD2E6BC2684031300A7D764 /* host2flutter.gen.m in Sources */, 0DD2E6BC2684031300A7D764 /* host2flutter.gen.m in Sources */,
0DD2E6BE2684031300A7D764 /* message.gen.m in Sources */, 0DD2E6BE2684031300A7D764 /* message.gen.m in Sources */,
@ -517,7 +527,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -596,7 +606,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -645,7 +655,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -8,24 +8,25 @@
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@interface Value () @interface Value ()
+ (Value*)fromMap:(NSDictionary*)dict; + (Value *)fromMap:(NSDictionary *)dict;
- (NSDictionary*)toMap; - (NSDictionary *)toMap;
@end @end
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@interface MockBinaryMessenger : NSObject<FlutterBinaryMessenger> @interface MockBinaryMessenger : NSObject<FlutterBinaryMessenger>
@property(nonatomic, copy) NSNumber* result; @property(nonatomic, copy) NSNumber *result;
@property(nonatomic, retain) FlutterStandardMessageCodec* codec; @property(nonatomic, retain) NSObject<FlutterMessageCodec> *codec;
@property(nonatomic, retain) NSMutableDictionary<NSString*, FlutterBinaryMessageHandler>* handlers; @property(nonatomic, retain) NSMutableDictionary<NSString *, FlutterBinaryMessageHandler> *handlers;
- (instancetype)init NS_UNAVAILABLE;
@end @end
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@implementation MockBinaryMessenger @implementation MockBinaryMessenger
- (instancetype)init { - (instancetype)initWithCodec:(NSObject<FlutterMessageCodec> *)codec {
self = [super init]; self = [super init];
if (self) { if (self) {
_codec = [FlutterStandardMessageCodec sharedInstance]; _codec = codec;
_handlers = [[NSMutableDictionary alloc] init]; _handlers = [[NSMutableDictionary alloc] init];
} }
return self; return self;
@ -34,21 +35,20 @@
- (void)cleanupConnection:(FlutterBinaryMessengerConnection)connection { - (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 - (void)sendOnChannel:(nonnull NSString *)channel
message:(NSData* _Nullable)message message:(NSData *_Nullable)message
binaryReply:(FlutterBinaryReply _Nullable)callback { binaryReply:(FlutterBinaryReply _Nullable)callback {
if (self.result) { if (self.result) {
Value* output = [[Value alloc] init]; Value *output = [[Value alloc] init];
output.number = self.result; output.number = self.result;
NSDictionary* outputDictionary = [output toMap]; callback([_codec encode:output]);
callback([_codec encode:outputDictionary]);
} }
} }
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel
binaryMessageHandler: binaryMessageHandler:
(FlutterBinaryMessageHandler _Nullable)handler { (FlutterBinaryMessageHandler _Nullable)handler {
_handlers[channel] = [handler copy]; _handlers[channel] = [handler copy];
@ -59,17 +59,17 @@
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@interface MockApi2Host : NSObject<Api2Host> @interface MockApi2Host : NSObject<Api2Host>
@property(nonatomic, copy) NSNumber* output; @property(nonatomic, copy) NSNumber *output;
@property(nonatomic, retain) FlutterError* voidVoidError; @property(nonatomic, retain) FlutterError *voidVoidError;
@end @end
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@implementation MockApi2Host @implementation MockApi2Host
- (void)calculate:(Value* _Nullable)input - (void)calculate:(Value *_Nullable)input
completion:(nonnull void (^)(Value* _Nullable, FlutterError* _Nullable))completion { completion:(nonnull void (^)(Value *_Nullable, FlutterError *_Nullable))completion {
if (self.output) { if (self.output) {
Value* output = [[Value alloc] init]; Value *output = [[Value alloc] init];
output.number = self.output; output.number = self.output;
completion(output, nil); completion(output, nil);
} else { } else {
@ -77,7 +77,7 @@
} }
} }
- (void)voidVoid:(nonnull void (^)(FlutterError* _Nullable))completion { - (void)voidVoid:(nonnull void (^)(FlutterError *_Nullable))completion {
completion(self.voidVoidError); completion(self.voidVoidError);
} }
@ -91,14 +91,15 @@
@implementation AsyncHandlersTest @implementation AsyncHandlersTest
- (void)testAsyncHost2Flutter { - (void)testAsyncHost2Flutter {
MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; MockBinaryMessenger *binaryMessenger =
[[MockBinaryMessenger alloc] initWithCodec:Api2FlutterGetCodec()];
binaryMessenger.result = @(2); binaryMessenger.result = @(2);
Api2Flutter* api2Flutter = [[Api2Flutter alloc] initWithBinaryMessenger:binaryMessenger]; Api2Flutter *api2Flutter = [[Api2Flutter alloc] initWithBinaryMessenger:binaryMessenger];
Value* input = [[Value alloc] init]; Value *input = [[Value alloc] init];
input.number = @(1); input.number = @(1);
XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"];
[api2Flutter calculate:input [api2Flutter calculate:input
completion:^(Value* _Nonnull output, NSError* _Nullable error) { completion:^(Value *_Nonnull output, NSError *_Nullable error) {
XCTAssertEqual(output.number.intValue, 2); XCTAssertEqual(output.number.intValue, 2);
[expectation fulfill]; [expectation fulfill];
}]; }];
@ -106,16 +107,17 @@
} }
- (void)testAsyncFlutter2HostVoidVoid { - (void)testAsyncFlutter2HostVoidVoid {
MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; MockBinaryMessenger *binaryMessenger =
MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()];
MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init];
mockApi2Host.output = @(2); mockApi2Host.output = @(2);
Api2HostSetup(binaryMessenger, mockApi2Host); Api2HostSetup(binaryMessenger, mockApi2Host);
NSString* channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; NSString *channelName = @"dev.flutter.pigeon.Api2Host.voidVoid";
XCTAssertNotNil(binaryMessenger.handlers[channelName]); XCTAssertNotNil(binaryMessenger.handlers[channelName]);
XCTestExpectation* expectation = [self expectationWithDescription:@"voidvoid callback"]; XCTestExpectation *expectation = [self expectationWithDescription:@"voidvoid callback"];
binaryMessenger.handlers[channelName](nil, ^(NSData* data) { binaryMessenger.handlers[channelName](nil, ^(NSData *data) {
NSDictionary* outputMap = [binaryMessenger.codec decode:data]; NSDictionary *outputMap = [binaryMessenger.codec decode:data];
XCTAssertEqualObjects(outputMap[@"result"], [NSNull null]); XCTAssertEqualObjects(outputMap[@"result"], [NSNull null]);
XCTAssertEqualObjects(outputMap[@"error"], [NSNull null]); XCTAssertEqualObjects(outputMap[@"error"], [NSNull null]);
[expectation fulfill]; [expectation fulfill];
@ -124,16 +126,17 @@
} }
- (void)testAsyncFlutter2HostVoidVoidError { - (void)testAsyncFlutter2HostVoidVoidError {
MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; MockBinaryMessenger *binaryMessenger =
MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()];
MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init];
mockApi2Host.voidVoidError = [FlutterError errorWithCode:@"code" message:@"message" details:nil]; mockApi2Host.voidVoidError = [FlutterError errorWithCode:@"code" message:@"message" details:nil];
Api2HostSetup(binaryMessenger, mockApi2Host); Api2HostSetup(binaryMessenger, mockApi2Host);
NSString* channelName = @"dev.flutter.pigeon.Api2Host.voidVoid"; NSString *channelName = @"dev.flutter.pigeon.Api2Host.voidVoid";
XCTAssertNotNil(binaryMessenger.handlers[channelName]); XCTAssertNotNil(binaryMessenger.handlers[channelName]);
XCTestExpectation* expectation = [self expectationWithDescription:@"voidvoid callback"]; XCTestExpectation *expectation = [self expectationWithDescription:@"voidvoid callback"];
binaryMessenger.handlers[channelName](nil, ^(NSData* data) { binaryMessenger.handlers[channelName](nil, ^(NSData *data) {
NSDictionary* outputMap = [binaryMessenger.codec decode:data]; NSDictionary *outputMap = [binaryMessenger.codec decode:data];
XCTAssertNotNil(outputMap[@"error"]); XCTAssertNotNil(outputMap[@"error"]);
XCTAssertEqualObjects(outputMap[@"error"][@"code"], mockApi2Host.voidVoidError.code); XCTAssertEqualObjects(outputMap[@"error"][@"code"], mockApi2Host.voidVoidError.code);
[expectation fulfill]; [expectation fulfill];
@ -142,20 +145,21 @@
} }
- (void)testAsyncFlutter2Host { - (void)testAsyncFlutter2Host {
MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; MockBinaryMessenger *binaryMessenger =
MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()];
MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init];
mockApi2Host.output = @(2); mockApi2Host.output = @(2);
Api2HostSetup(binaryMessenger, mockApi2Host); Api2HostSetup(binaryMessenger, mockApi2Host);
NSString* channelName = @"dev.flutter.pigeon.Api2Host.calculate"; NSString *channelName = @"dev.flutter.pigeon.Api2Host.calculate";
XCTAssertNotNil(binaryMessenger.handlers[channelName]); XCTAssertNotNil(binaryMessenger.handlers[channelName]);
Value* input = [[Value alloc] init]; Value *input = [[Value alloc] init];
input.number = @(1); input.number = @(1);
NSData* inputEncoded = [binaryMessenger.codec encode:[input toMap]]; NSData *inputEncoded = [binaryMessenger.codec encode:input];
XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"];
binaryMessenger.handlers[channelName](inputEncoded, ^(NSData* data) { binaryMessenger.handlers[channelName](inputEncoded, ^(NSData *data) {
NSDictionary* outputMap = [binaryMessenger.codec decode:data]; NSDictionary *outputMap = [binaryMessenger.codec decode:data];
Value* output = [Value fromMap:outputMap[@"result"]]; Value *output = outputMap[@"result"];
XCTAssertEqual(output.number.intValue, 2); XCTAssertEqual(output.number.intValue, 2);
[expectation fulfill]; [expectation fulfill];
}); });
@ -163,18 +167,19 @@
} }
- (void)testAsyncFlutter2HostError { - (void)testAsyncFlutter2HostError {
MockBinaryMessenger* binaryMessenger = [[MockBinaryMessenger alloc] init]; MockBinaryMessenger *binaryMessenger =
MockApi2Host* mockApi2Host = [[MockApi2Host alloc] init]; [[MockBinaryMessenger alloc] initWithCodec:Api2HostGetCodec()];
MockApi2Host *mockApi2Host = [[MockApi2Host alloc] init];
Api2HostSetup(binaryMessenger, mockApi2Host); Api2HostSetup(binaryMessenger, mockApi2Host);
NSString* channelName = @"dev.flutter.pigeon.Api2Host.calculate"; NSString *channelName = @"dev.flutter.pigeon.Api2Host.calculate";
XCTAssertNotNil(binaryMessenger.handlers[channelName]); XCTAssertNotNil(binaryMessenger.handlers[channelName]);
Value* input = [[Value alloc] init]; Value *input = [[Value alloc] init];
input.number = @(1); input.number = @(1);
NSData* inputEncoded = [binaryMessenger.codec encode:[input toMap]]; NSData *inputEncoded = [binaryMessenger.codec encode:[input toMap]];
XCTestExpectation* expectation = [self expectationWithDescription:@"calculate callback"]; XCTestExpectation *expectation = [self expectationWithDescription:@"calculate callback"];
binaryMessenger.handlers[channelName](inputEncoded, ^(NSData* data) { binaryMessenger.handlers[channelName](inputEncoded, ^(NSData *data) {
NSDictionary* outputMap = [binaryMessenger.codec decode:data]; NSDictionary *outputMap = [binaryMessenger.codec decode:data];
XCTAssertNotNil(outputMap[@"error"]); XCTAssertNotNil(outputMap[@"error"]);
[expectation fulfill]; [expectation fulfill];
}); });

View File

@ -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 <Flutter/Flutter.h>
#import <XCTest/XCTest.h>
#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

View File

@ -249,6 +249,7 @@ run_ios_unittests() {
gen_ios_unittests_code ./pigeons/host2flutter.dart "" gen_ios_unittests_code ./pigeons/host2flutter.dart ""
gen_ios_unittests_code ./pigeons/list.dart "LST" gen_ios_unittests_code ./pigeons/list.dart "LST"
gen_ios_unittests_code ./pigeons/message.dart "" 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_flutter.dart "VAF"
gen_ios_unittests_code ./pigeons/void_arg_host.dart "VAH" gen_ios_unittests_code ./pigeons/void_arg_host.dart "VAH"
gen_ios_unittests_code ./pigeons/voidflutter.dart "VF" 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/java_double_host_api.dart JavaDoubleHostApi
gen_android_unittests_code ./pigeons/list.dart PigeonList gen_android_unittests_code ./pigeons/list.dart PigeonList
gen_android_unittests_code ./pigeons/message.dart MessagePigeon 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_flutter.dart VoidArgFlutter
gen_android_unittests_code ./pigeons/void_arg_host.dart VoidArgHost gen_android_unittests_code ./pigeons/void_arg_host.dart VoidArgHost
gen_android_unittests_code ./pigeons/voidflutter.dart VoidFlutter gen_android_unittests_code ./pigeons/voidflutter.dart VoidFlutter

View File

@ -392,7 +392,7 @@ void main() {
expect(mainCode, contains('class Api {')); expect(mainCode, contains('class Api {'));
expect(mainCode, isNot(contains('abstract class ApiMock'))); expect(mainCode, isNot(contains('abstract class ApiMock')));
expect(mainCode, isNot(contains('.ApiMock.doSomething'))); 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 <Object, Object>{};'))); expect(mainCode, isNot(contains('return <Object, Object>{};')));
generateTestDart( generateTestDart(
const DartOptions(isNullSafe: false), root, testCodeSink, "fo'o.dart"); const DartOptions(isNullSafe: false), root, testCodeSink, "fo'o.dart");
@ -401,7 +401,7 @@ void main() {
expect(testCode, isNot(contains('class Api {'))); expect(testCode, isNot(contains('class Api {')));
expect(testCode, contains('abstract class ApiMock')); expect(testCode, contains('abstract class ApiMock'));
expect(testCode, isNot(contains('.ApiMock.doSomething'))); expect(testCode, isNot(contains('.ApiMock.doSomething')));
expect(testCode, contains('\'${Keys.result}\': output.encode()')); expect(testCode, contains('\'${Keys.result}\': output'));
expect(testCode, contains('return <Object, Object>{};')); expect(testCode, contains('return <Object, Object>{};'));
}); });

View File

@ -357,7 +357,7 @@ void main() {
expect( expect(
code, code,
contains( 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)')); expect(code, contains('channel.setMessageHandler(null)'));
}); });

View File

@ -409,7 +409,7 @@ void main() {
generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink); generateObjcSource(const ObjcOptions(prefix: 'ABC'), root, sink);
final String code = sink.toString(); final String code = sink.toString();
expect(code, contains('ABCInput fromMap')); expect(code, contains('ABCInput fromMap'));
expect(code, matches('ABCInput.*=.*ABCInput fromMap')); expect(code, matches('ABCInput.*=.*message'));
expect(code, contains('void ABCApiSetup(')); expect(code, contains('void ABCApiSetup('));
}); });

View File

@ -307,28 +307,6 @@ abstract class NestorApi {
expect(classNames.contains('OnlyVisibleFromNesting'), true); 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', () { test('null safety flag', () {
final PigeonOptions results = final PigeonOptions results =
Pigeon.parseArgs(<String>['--dart_null_safety']); Pigeon.parseArgs(<String>['--dart_null_safety']);
@ -546,32 +524,6 @@ abstract class Api {
expect(results.errors[0].message, contains('Nullable')); 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', () { test('test invalid import', () {
const String code = 'import \'foo.dart\';\n'; const String code = 'import \'foo.dart\';\n';
final ParseResults results = _parseSource(code); final ParseResults results = _parseSource(code);