diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 2c96dc78161c..0e5faeed09e7 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.21 + +* Fixed decode method on generated Flutter classes that use null-safety and have + null values. + ## 0.1.20 * Implemented `@async` HostApi's for iOS. diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 6cf834db39dc..ecfe5ef58b72 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -178,11 +178,11 @@ void _writeFlutterApi( String _addGenericTypes(String dataType, String nullTag) { switch (dataType) { case 'List': - return 'List'; + return 'List$nullTag'; case 'Map': - return 'Map'; + return 'Map$nullTag'; default: - return dataType; + return '$dataType$nullTag'; } } @@ -212,7 +212,7 @@ void generateDart(DartOptions opt, Root root, StringSink sink) { indent.scoped('{', '}', () { for (Field field in klass.fields) { final String datatype = - '${_addGenericTypes(field.dataType, nullTag)}$nullTag'; + '${_addGenericTypes(field.dataType, nullTag)}'; indent.writeln('$datatype ${field.name};'); } if (klass.fields.isNotEmpty) { diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 996e794a08ac..d196e26a21d7 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. This must match the version in pubspec.yaml. -const String pigeonVersion = '0.1.20'; +const String pigeonVersion = '0.1.21'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.gitignore b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.gitignore new file mode 100644 index 000000000000..0fa6b675c0a5 --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.metadata b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.metadata new file mode 100644 index 000000000000..5db57c1fab41 --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 6db3d61ed4a58ba89140d7fe1fd294b598cc29c5 + channel: master + +project_type: app diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/README.md b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/README.md new file mode 100644 index 000000000000..3c52fc95e5c6 --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/README.md @@ -0,0 +1,3 @@ +# flutter_unit_tests + +Unit test scaffold for null safe Flutter projects. 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 new file mode 100644 index 000000000000..455058d5a09e --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart @@ -0,0 +1,166 @@ +// Autogenerated from Pigeon (v0.1.21), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/services.dart'; + +class SearchReply { + String? result; + String? error; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['result'] = result; + pigeonMap['error'] = error; + return pigeonMap; + } + + static SearchReply decode(Object message) { + final Map pigeonMap = message as Map; + return SearchReply() + ..result = pigeonMap['result'] as String? + ..error = pigeonMap['error'] as String?; + } +} + +class SearchRequest { + String? query; + int? anInt; + bool? aBool; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['query'] = query; + pigeonMap['anInt'] = anInt; + pigeonMap['aBool'] = aBool; + return pigeonMap; + } + + static SearchRequest decode(Object message) { + final Map pigeonMap = message as Map; + return SearchRequest() + ..query = pigeonMap['query'] as String? + ..anInt = pigeonMap['anInt'] as int? + ..aBool = pigeonMap['aBool'] as bool?; + } +} + +class Nested { + SearchRequest? request; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['request'] = request == null ? null : request!.encode(); + return pigeonMap; + } + + static Nested decode(Object message) { + final Map pigeonMap = message as Map; + return Nested() + ..request = pigeonMap['request'] != null + ? SearchRequest.decode(pigeonMap['request']!) + : null; + } +} + +abstract class FlutterSearchApi { + SearchReply search(SearchRequest arg); + static void setup(FlutterSearchApi? api) { + { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FlutterSearchApi.search', StandardMessageCodec()); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null. Expected SearchRequest.'); + final SearchRequest input = SearchRequest.decode(message!); + final SearchReply output = api.search(input); + return output.encode(); + }); + } + } + } +} + +class NestedApi { + Future search(Nested arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NestedApi.search', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) 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 SearchReply.decode(replyMap['result']!); + } + } +} + +class Api { + Future initialize() async { + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.Api.initialize', StandardMessageCodec()); + final Map? replyMap = + await channel.send(null) 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 { + // noop + } + } + + Future search(SearchRequest arg) async { + final Object encoded = arg.encode(); + const BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.Api.search', StandardMessageCodec()); + final Map? replyMap = + await channel.send(encoded) 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 SearchReply.decode(replyMap['result']!); + } + } +} diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/pubspec.yaml b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/pubspec.yaml new file mode 100644 index 000000000000..32058dea3d61 --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/pubspec.yaml @@ -0,0 +1,17 @@ +name: flutter_unit_tests +description: Unit test scaffold for null safe Flutter projects. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev +version: 1.0.0+1 +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true 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 new file mode 100644 index 000000000000..523127a3cfbf --- /dev/null +++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart @@ -0,0 +1,28 @@ +// 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 'package:flutter_unit_tests/null_safe_pigeon.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('with values filled', () { + final SearchReply reply = SearchReply() + ..result = 'foo' + ..error = 'bar'; + final Object encoded = reply.encode(); + final SearchReply decoded = SearchReply.decode(encoded); + expect(reply.result, decoded.result); + expect(reply.error, decoded.error); + }); + + test('with null value', () { + final SearchReply reply = SearchReply() + ..result = 'foo' + ..error = null; + final Object encoded = reply.encode(); + final SearchReply decoded = SearchReply.decode(encoded); + expect(reply.result, decoded.result); + expect(reply.error, decoded.error); + }); +} diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 408779eaad95..e534e2d57da3 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -1,5 +1,5 @@ name: pigeon -version: 0.1.20 # This must match the version in lib/generator_tools.dart +version: 0.1.21 # This must match the version in lib/generator_tools.dart 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/run_tests.sh b/packages/pigeon/run_tests.sh index c6b5b20c5285..ae5aa1e8f7df 100755 --- a/packages/pigeon/run_tests.sh +++ b/packages/pigeon/run_tests.sh @@ -110,6 +110,19 @@ pub run test test/ ############################################################################### pub run pigeon 1> /dev/null +############################################################################### +# Run unit tests on generated Dart code. +############################################################################### +pushd $PWD +pub run pigeon \ + --input pigeons/message.dart \ + --dart_null_safety \ + --dart_out platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart +cd platform_tests/flutter_null_safe_unit_tests +flutter pub get +flutter test test/null_safe_test.dart +popd + ############################################################################### # Mock handler flutter tests. ###############################################################################