Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ linter:
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
# - avoid_as # conflicts with NNBD
# - avoid_as # no longer relevant with null safety
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
Expand Down
5 changes: 5 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.1.19

* Fixed a bug introduced in 0.1.17 where methods without arguments were
no longer being called.

## 0.1.18

* Null safe requires Dart 2.12.
Expand Down
19 changes: 9 additions & 10 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void _writeFlutterApi(
}) {
assert(api.location == ApiLocation.flutter);
final String nullTag = opt.isNullSafe ? '?' : '';
final String unwrapOperator = opt.isNullSafe ? '!' : '';
indent.write('abstract class ${api.name} ');
indent.scoped('{', '}', () {
for (Method func in api.methods) {
Expand All @@ -106,10 +107,10 @@ void _writeFlutterApi(
indent.writeln(
'const BasicMessageChannel<Object$nullTag> channel =',
);
final String channelName = channelNameFunc == null
? makeChannelName(api, func)
: channelNameFunc(func);
indent.nest(2, () {
final String channelName = channelNameFunc == null
? makeChannelName(api, func)
: channelNameFunc(func);
indent.writeln(
'BasicMessageChannel<Object$nullTag>(\'$channelName\', StandardMessageCodec());',
);
Expand All @@ -134,16 +135,16 @@ void _writeFlutterApi(
: func.returnType == 'void'
? 'return;'
: 'return null;';
indent.write('if (message == null) ');
indent.scoped('{', '}', () {
indent.writeln(emptyReturnStatement);
});
String call;
if (argType == 'void') {
indent.writeln('// ignore message');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is message always null when argType is void?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean in pigeon-generated code? Or?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup. pigeon-generated code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like _writeHostApi always sends null if the argType is void, but of course people can always bypass pigeon and send whatever.

call = 'api.${func.name}()';
} else {
indent.writeln(
'final $argType input = $argType.decode(message);',
'assert(message != null, \'Argument for $channelName was null. Expected $argType.\');',
);
indent.writeln(
'final $argType input = $argType.decode(message$unwrapOperator);',
);
call = 'api.${func.name}(input)';
}
Expand Down Expand Up @@ -213,7 +214,6 @@ void generateDart(DartOptions opt, Root root, StringSink sink) {
if (klass.fields.isNotEmpty) {
indent.writeln('');
}
indent.writeln('// ignore: unused_element');
indent.write('Object encode() ');
indent.scoped('{', '}', () {
indent.writeln(
Expand All @@ -232,7 +232,6 @@ void generateDart(DartOptions opt, Root root, StringSink sink) {
indent.writeln('return pigeonMap;');
});
indent.writeln('');
indent.writeln('// ignore: unused_element');
indent.write(
'static ${klass.name} decode(Object message) ',
);
Expand Down
4 changes: 2 additions & 2 deletions packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import 'dart:io';
import 'dart:mirrors';
import 'ast.dart';

/// The current version of pigeon.
const String pigeonVersion = '0.1.18';
/// The current version of pigeon. This must match the version in pubspec.yaml.
const String pigeonVersion = '0.1.19';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
38 changes: 28 additions & 10 deletions packages/pigeon/mock_handler_tester/test/message.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
// Autogenerated from Pigeon (v0.1.15), do not edit directly.
// Autogenerated from Pigeon (v0.1.19), 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.8
import 'dart:async';
import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;

import 'package:flutter/services.dart';

class SearchReply {
String result;
String error;

// ignore: unused_element
Object encode() {
final Map<Object, Object> pigeonMap = <Object, Object>{};
pigeonMap['result'] = result;
pigeonMap['error'] = error;
return pigeonMap;
}

// ignore: unused_element
static SearchReply decode(Object message) {
final Map<Object, Object> pigeonMap = message as Map<Object, Object>;
return SearchReply()
Expand All @@ -32,7 +31,6 @@ class SearchRequest {
int anInt;
bool aBool;

// ignore: unused_element
Object encode() {
final Map<Object, Object> pigeonMap = <Object, Object>{};
pigeonMap['query'] = query;
Expand All @@ -41,7 +39,6 @@ class SearchRequest {
return pigeonMap;
}

// ignore: unused_element
static SearchRequest decode(Object message) {
final Map<Object, Object> pigeonMap = message as Map<Object, Object>;
return SearchRequest()
Expand All @@ -54,14 +51,12 @@ class SearchRequest {
class Nested {
SearchRequest request;

// ignore: unused_element
Object encode() {
final Map<Object, Object> pigeonMap = <Object, Object>{};
pigeonMap['request'] = request == null ? null : request.encode();
return pigeonMap;
}

// ignore: unused_element
static Nested decode(Object message) {
final Map<Object, Object> pigeonMap = message as Map<Object, Object>;
return Nested()
Expand All @@ -81,9 +76,8 @@ abstract class FlutterSearchApi {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object message) async {
if (message == null) {
return null;
}
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();
Expand Down Expand Up @@ -121,6 +115,30 @@ class NestedApi {
}

class Api {
Future<void> initialize() async {
const BasicMessageChannel<Object> channel = BasicMessageChannel<Object>(
'dev.flutter.pigeon.Api.initialize', StandardMessageCodec());
final Map<Object, Object> replyMap =
await channel.send(null) 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 {
// noop
}
}

Future<SearchReply> search(SearchRequest arg) async {
final Object encoded = arg.encode();
const BasicMessageChannel<Object> channel = BasicMessageChannel<Object>(
Expand Down
26 changes: 19 additions & 7 deletions packages/pigeon/mock_handler_tester/test/test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v0.1.15), do not edit directly.
// Autogenerated from Pigeon (v0.1.19), 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.8
Expand All @@ -19,9 +19,8 @@ abstract class TestNestedApi {
channel.setMockMessageHandler(null);
} else {
channel.setMockMessageHandler((Object message) async {
if (message == null) {
return <Object, Object>{};
}
assert(message != null,
'Argument for dev.flutter.pigeon.NestedApi.search was null. Expected Nested.');
final Nested input = Nested.decode(message);
final SearchReply output = api.search(input);
return <Object, Object>{'result': output.encode()};
Expand All @@ -32,18 +31,31 @@ abstract class TestNestedApi {
}

abstract class TestHostApi {
void initialize();
SearchReply search(SearchRequest arg);
static void setup(TestHostApi api) {
{
const BasicMessageChannel<Object> channel = BasicMessageChannel<Object>(
'dev.flutter.pigeon.Api.initialize', StandardMessageCodec());
if (api == null) {
channel.setMockMessageHandler(null);
} else {
channel.setMockMessageHandler((Object message) async {
// ignore message
api.initialize();
return <Object, Object>{};
});
}
}
{
const BasicMessageChannel<Object> channel = BasicMessageChannel<Object>(
'dev.flutter.pigeon.Api.search', StandardMessageCodec());
if (api == null) {
channel.setMockMessageHandler(null);
} else {
channel.setMockMessageHandler((Object message) async {
if (message == null) {
return <Object, Object>{};
}
assert(message != null,
'Argument for dev.flutter.pigeon.Api.search was null. Expected SearchRequest.');
final SearchRequest input = SearchRequest.decode(message);
final SearchReply output = api.search(input);
return <Object, Object>{'result': output.encode()};
Expand Down
56 changes: 53 additions & 3 deletions packages/pigeon/mock_handler_tester/test/widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@
// 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:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import 'message.dart';
import 'test.dart';

class Mock implements TestHostApi {
bool didCall = false;
List<String> log = <String>[];

@override
void initialize() {
log.add('initialize');
}

@override
SearchReply search(SearchRequest arg) {
didCall = true;
log.add('search');
return SearchReply()..result = arg.query;
}
}
Expand Down Expand Up @@ -46,7 +55,48 @@ void main() {
final Mock mock = Mock();
TestHostApi.setup(mock);
final SearchReply reply = await api.search(SearchRequest()..query = 'foo');
expect(mock.didCall, true);
expect(mock.log, <String>['search']);
expect(reply.result, 'foo');
});

test('no-arg calls', () async {
final Api api = Api();
final Mock mock = Mock();
TestHostApi.setup(mock);
await api.initialize();
expect(mock.log, <String>['initialize']);
});

test(
'calling methods with null',
() async {
final Mock mock = Mock();
TestHostApi.setup(mock);
expect(
await const BasicMessageChannel<Object>(
'dev.flutter.pigeon.Api.initialize',
StandardMessageCodec(),
).send(null),
isEmpty,
);
try {
await const BasicMessageChannel<Object>(
'dev.flutter.pigeon.Api.search',
StandardMessageCodec(),
).send(null) as Map<Object, Object>;
expect(true, isFalse); // should not reach here
} catch (error) {
expect(error, isAssertionError);
expect(
error.toString(),
contains(
'Argument for dev.flutter.pigeon.Api.search was null. Expected SearchRequest.',
),
);
}
expect(mock.log, <String>['initialize']);
},
// TODO(ianh): skip can be removed after first stable release in 2021
skip: Platform.environment['CHANNEL'] == 'stable',
);
}
1 change: 1 addition & 0 deletions packages/pigeon/pigeons/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class SearchReply {

@HostApi(dartHostTestHandler: 'TestHostApi')
abstract class Api {
void initialize();
SearchReply search(SearchRequest request);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: pigeon
version: 0.1.18
version: 0.1.19 # 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:
Expand Down
5 changes: 4 additions & 1 deletion packages/pigeon/test/dart_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ void main() {
final StringBuffer sink = StringBuffer();
generateDart(DartOptions(), root, sink);
final String code = sink.toString();
expect(code, isNot(matches('=.*doSomething')));
// The next line verifies that we're not setting a variable to the value of "doSomething", but
// ignores the line where we assert the value of the argument isn't null, since on that line
// we mention "doSomething" in the assertion message.
expect(code, isNot(matches('[^!]=.*doSomething')));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit: some comment would be helpful. It took me a while to figure out what it was checking :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair, me too...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

expect(code, contains('doSomething('));
expect(code, isNot(contains('.encode()')));
});
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/test/version_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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 RegExp regex = RegExp('version:\s*(.*?) #');
final RegExpMatch match = regex.firstMatch(pubspec);
expect(match, isNotNull);
expect(pigeonVersion, match.group(1).trim());
Expand Down