diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index ba7a33836b..f3622b48ab 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.1.4 + +* Fixed nullability for NSError's in generated objc code. +* Fixed nullability of nested objects in the Dart generator. +* Added test to make sure the pigeon version is correct in generated code headers. + ## 0.1.3 * Added error message if supported datatypes are used as arguments or return diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 989f9d3a92..baea13ecb3 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -151,7 +151,8 @@ void generateDart(Root root, StringSink sink) { for (Field field in klass.fields) { indent.write('pigeonMap[\'${field.name}\'] = '); if (customClassNames.contains(field.dataType)) { - indent.addln('${field.name}._toMap();'); + indent.addln( + '${field.name} == null ? null : ${field.name}._toMap();'); } else { indent.addln('${field.name};'); } @@ -162,6 +163,10 @@ void generateDart(Root root, StringSink sink) { indent.write( 'static ${klass.name} _fromMap(Map pigeonMap) '); indent.scoped('{', '}', () { + indent.write('if (pigeonMap == null)'); + indent.scoped('{', '}', () { + indent.writeln('return null;'); + }); indent.writeln('final ${klass.name} result = ${klass.name}();'); for (Field field in klass.fields) { indent.write('result.${field.name} = '); diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index c65c2f2509..db3157c7ff 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -8,7 +8,7 @@ import 'dart:mirrors'; import 'ast.dart'; /// The current version of pigeon. -const String pigeonVersion = '0.1.2'; +const String pigeonVersion = '0.1.4'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index fb52f0b9c3..e264ef35e2 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -28,8 +28,8 @@ String _className(String prefix, String className) { String _callbackForType(String dartType, String objcType) { return dartType == 'void' - ? 'void(^)(NSError*)' - : 'void(^)($objcType*, NSError*)'; + ? 'void(^)(NSError* _Nullable)' + : 'void(^)($objcType*, NSError* _Nullable)'; } const Map _objcTypeForDartTypeMap = { diff --git a/packages/pigeon/mock_handler_tester/test/message.dart b/packages/pigeon/mock_handler_tester/test/message.dart index 9f0db48b7e..1aa33c6494 100644 --- a/packages/pigeon/mock_handler_tester/test/message.dart +++ b/packages/pigeon/mock_handler_tester/test/message.dart @@ -17,6 +17,9 @@ class SearchReply { // ignore: unused_element static SearchReply _fromMap(Map pigeonMap) { + if (pigeonMap == null) { + return null; + } final SearchReply result = SearchReply(); result.result = pigeonMap['result']; result.error = pigeonMap['error']; @@ -39,6 +42,9 @@ class SearchRequest { // ignore: unused_element static SearchRequest _fromMap(Map pigeonMap) { + if (pigeonMap == null) { + return null; + } final SearchRequest result = SearchRequest(); result.query = pigeonMap['query']; result.anInt = pigeonMap['anInt']; @@ -52,12 +58,15 @@ class Nested { // ignore: unused_element Map _toMap() { final Map pigeonMap = {}; - pigeonMap['request'] = request._toMap(); + pigeonMap['request'] = request == null ? null : request._toMap(); return pigeonMap; } // ignore: unused_element static Nested _fromMap(Map pigeonMap) { + if (pigeonMap == null) { + return null; + } final Nested result = Nested(); result.request = SearchRequest._fromMap(pigeonMap['request']); return result; @@ -105,6 +114,23 @@ class NestedApi { } } +abstract class TestNestedApi { + SearchReply search(Nested arg); + static void setup(TestNestedApi api) { + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NestedApi.search', StandardMessageCodec()); + channel.setMockMessageHandler((dynamic message) async { + final Map mapMessage = + message as Map; + final Nested input = Nested._fromMap(mapMessage); + final SearchReply output = api.search(input); + return {'result': output._toMap()}; + }); + } + } +} + class Api { Future search(SearchRequest arg) async { final Map requestMap = arg._toMap(); diff --git a/packages/pigeon/mock_handler_tester/test/widget_test.dart b/packages/pigeon/mock_handler_tester/test/widget_test.dart index 82f652a8ef..41322b3e9e 100644 --- a/packages/pigeon/mock_handler_tester/test/widget_test.dart +++ b/packages/pigeon/mock_handler_tester/test/widget_test.dart @@ -14,10 +14,32 @@ class Mock implements TestHostApi { } } +class MockNested implements TestNestedApi { + bool didCall = false; + @override + SearchReply search(Nested arg) { + didCall = true; + if (arg.request == null) { + return SearchReply(); + } else { + return SearchReply()..result = arg.request.query; + } + } +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); - test('description', () async { + test('simple', () async { + final NestedApi api = NestedApi(); + final MockNested mock = MockNested(); + TestNestedApi.setup(mock); + final SearchReply reply = await api.search(Nested()..request = null); + expect(mock.didCall, true); + expect(reply.result, null); + }); + + test('nested', () async { final Api api = Api(); final Mock mock = Mock(); TestHostApi.setup(mock); diff --git a/packages/pigeon/pigeons/message.dart b/packages/pigeon/pigeons/message.dart index 73dc13843e..2dff2f090e 100644 --- a/packages/pigeon/pigeons/message.dart +++ b/packages/pigeon/pigeons/message.dart @@ -27,7 +27,7 @@ class Nested { SearchRequest request; } -@HostApi() +@HostApi(dartHostTestHandler: 'TestNestedApi') abstract class NestedApi { SearchReply search(Nested nested); } diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 616b1a5e5a..8e7b9f0369 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -1,5 +1,5 @@ name: pigeon -version: 0.1.3 +version: 0.1.4 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. homepage: https://github.com/flutter/packages/tree/master/packages/pigeon dependencies: diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index 0089145885..296b275c1c 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -58,7 +58,10 @@ void main() { final StringBuffer sink = StringBuffer(); generateDart(root, sink); final String code = sink.toString(); - expect(code, contains('pigeonMap[\'nested\'] = nested._toMap()')); + expect( + code, + contains( + 'pigeonMap[\'nested\'] = nested == null ? null : nested._toMap()')); expect(code, contains('result.nested = Input._fromMap(pigeonMap[\'nested\']);')); }); diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart index 74ade0fdfe..1acbcc5680 100644 --- a/packages/pigeon/test/objc_generator_test.dart +++ b/packages/pigeon/test/objc_generator_test.dart @@ -315,7 +315,7 @@ void main() { final StringBuffer sink = StringBuffer(); generateObjcHeader(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink); final String code = sink.toString(); - expect(code, contains('completion:(void(^)(NSError*))')); + expect(code, contains('completion:(void(^)(NSError* _Nullable))')); }); test('gen flutter void return source', () { @@ -331,7 +331,7 @@ void main() { final StringBuffer sink = StringBuffer(); generateObjcSource(ObjcOptions(header: 'foo.h', prefix: 'ABC'), root, sink); final String code = sink.toString(); - expect(code, contains('completion:(void(^)(NSError*))')); + expect(code, contains('completion:(void(^)(NSError* _Nullable))')); expect(code, contains('completion(nil)')); }); @@ -383,7 +383,7 @@ void main() { expect( code, contains( - '(void)doSomething:(void(^)(ABCOutput*, NSError*))completion')); + '(void)doSomething:(void(^)(ABCOutput*, NSError* _Nullable))completion')); }); test('gen flutter void arg header', () { @@ -402,7 +402,7 @@ void main() { expect( code, contains( - '(void)doSomething:(void(^)(ABCOutput*, NSError*))completion')); + '(void)doSomething:(void(^)(ABCOutput*, NSError* _Nullable))completion')); expect(code, contains('channel sendMessage:nil')); }); diff --git a/packages/pigeon/test/version_test.dart b/packages/pigeon/test/version_test.dart new file mode 100644 index 0000000000..70e4382a12 --- /dev/null +++ b/packages/pigeon/test/version_test.dart @@ -0,0 +1,19 @@ +// Copyright 2020 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 'dart:io'; + +import 'package:pigeon/generator_tools.dart'; +import 'package:test/test.dart'; + +void main() { + test('pigeon version matches pubspec', () { + final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; + final String pubspec = File(pubspecPath).readAsStringSync(); + final RegExp regex = RegExp('version:\s*(.*)'); + final RegExpMatch match = regex.firstMatch(pubspec); + expect(match, isNotNull); + expect(pigeonVersion, match.group(1).trim()); + }); +}