Skip to content

Commit 8ed8327

Browse files
[pigeon] Add initial FlutterApi integration tests (#2901)
* Add trivial async test as a baseline for the scaffolding * Kotlin generator async void fix * Add initial round-trip tests * Windows fixes * Cleanup * Version bump for bug fix * Fix analysis * Version bump in generator
1 parent a8809d7 commit 8ed8327

File tree

14 files changed

+429
-31
lines changed

14 files changed

+429
-31
lines changed

packages/pigeon/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 4.2.9
2+
3+
* [kotlin] Fixes a bug with some methods that return `void`.
4+
15
## 4.2.8
26

37
* Adds the ability to use `runWithOptions` entrypoint to allow external libraries to use the pigeon easier.

packages/pigeon/lib/generator_tools.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'dart:mirrors';
99
import 'ast.dart';
1010

1111
/// The current version of pigeon. This must match the version in pubspec.yaml.
12-
const String pigeonVersion = '4.2.8';
12+
const String pigeonVersion = '4.2.9';
1313

1414
/// Read all the content from [stdin] to a String.
1515
String readStdin() {

packages/pigeon/lib/kotlin_generator.dart

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,11 @@ void _writeHostApi(Indent indent, Api api, Root root) {
233233
'api.${method.name}(${methodArgument.join(', ')})';
234234
if (method.isAsynchronous) {
235235
indent.write('$call ');
236-
if (method.returnType.isVoid) {
237-
indent.scoped('{', '}', () {
238-
indent.writeln('reply.reply(null)');
239-
});
240-
} else {
241-
indent.scoped('{', '}', () {
242-
indent.writeln('reply.reply(wrapResult(it))');
243-
});
244-
}
236+
final String resultValue =
237+
method.returnType.isVoid ? 'null' : 'it';
238+
indent.scoped('{', '}', () {
239+
indent.writeln('reply.reply(wrapResult($resultValue))');
240+
});
245241
} else if (method.returnType.isVoid) {
246242
indent.writeln(call);
247243
indent.writeln('wrapped["${Keys.result}"] = null');

packages/pigeon/pigeons/core_tests.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class AllTypesWrapper {
3333
/// platform_test integration tests.
3434
@HostApi()
3535
abstract class HostIntegrationCoreApi {
36+
// ========== Syncronous method tests ==========
37+
3638
/// A no-op function taking no arguments and returning no value, to sanity
3739
/// test basic calling.
3840
void noop();
@@ -54,13 +56,31 @@ abstract class HostIntegrationCoreApi {
5456
@ObjCSelector('createNestedObjectWithString:')
5557
AllTypesWrapper createNestedString(String string);
5658

57-
// TODO(stuartmorgan): Add wrapper methods to trigger calls back into
58-
// FlutterIntegrationCore methods, to allow Dart-driven integration testing
59-
// of host->Dart calls. Each wrapper would be implemented by calling the
60-
// corresponding FlutterIntegrationCore method, passing arguments and return
61-
// values along unchanged. Since these will need to be async, we also need
62-
// async host API tests here, so that failures in Dart->host async calling
63-
// don't only show up here.
59+
// ========== Asyncronous method tests ==========
60+
61+
/// A no-op function taking no arguments and returning no value, to sanity
62+
/// test basic asynchronous calling.
63+
@async
64+
void noopAsync();
65+
66+
/// Returns the passed string asynchronously.
67+
@async
68+
@ObjCSelector('echoAsyncString:')
69+
String echoAsyncString(String aString);
70+
71+
// ========== Flutter API test wrappers ==========
72+
73+
@async
74+
void callFlutterNoop();
75+
76+
@async
77+
@ObjCSelector('callFlutterEchoString:')
78+
String callFlutterEchoString(String aString);
79+
80+
// TODO(stuartmorgan): Add callFlutterEchoString and the associated test once
81+
// either https://github.com/flutter/flutter/issues/116117 is fixed, or the
82+
// problematic type is moved out of AllTypes and into its own test, since
83+
// the type mismatch breaks the second `encode` round.
6484
}
6585

6686
/// The core interface that the Dart platform_test code implements for host
@@ -74,6 +94,10 @@ abstract class FlutterIntegrationCoreApi {
7494
/// Returns the passed object, to test serialization and deserialization.
7595
@ObjCSelector('echoAllTypes:')
7696
AllTypes echoAllTypes(AllTypes everything);
97+
98+
/// Returns the passed string, to test serialization and deserialization.
99+
@ObjCSelector('echoString:')
100+
String echoString(String aString);
77101
}
78102

79103
/// An API that can be implemented for minimal, compile-only tests.

packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88
import androidx.annotation.Nullable;
99
import com.example.alternate_language_test_plugin.CoreTests.AllTypes;
1010
import com.example.alternate_language_test_plugin.CoreTests.AllTypesWrapper;
11+
import com.example.alternate_language_test_plugin.CoreTests.FlutterIntegrationCoreApi;
1112
import com.example.alternate_language_test_plugin.CoreTests.HostIntegrationCoreApi;
13+
import com.example.alternate_language_test_plugin.CoreTests.Result;
1214
import io.flutter.embedding.engine.plugins.FlutterPlugin;
1315

1416
/** This plugin handles the native side of the integration tests in example/integration_test/. */
1517
public class AlternateLanguageTestPlugin implements FlutterPlugin, HostIntegrationCoreApi {
18+
@Nullable FlutterIntegrationCoreApi flutterApi = null;
19+
1620
@Override
1721
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
1822
HostIntegrationCoreApi.setup(binding.getBinaryMessenger(), this);
23+
flutterApi = new FlutterIntegrationCoreApi(binding.getBinaryMessenger());
1924
}
2025

2126
@Override
@@ -46,4 +51,35 @@ public void throwError() {
4651
AllTypes innerObject = new AllTypes.Builder().setAString(string).build();
4752
return new AllTypesWrapper.Builder().setValues(innerObject).build();
4853
}
54+
55+
@Override
56+
public void noopAsync(Result<Void> result) {
57+
result.success(null);
58+
}
59+
60+
@Override
61+
public void echoAsyncString(@NonNull String aString, Result<String> result) {
62+
result.success(aString);
63+
}
64+
65+
@Override
66+
public void callFlutterNoop(Result<Void> result) {
67+
flutterApi.noop(
68+
new FlutterIntegrationCoreApi.Reply<Void>() {
69+
public void reply(Void value) {
70+
result.success(value);
71+
}
72+
});
73+
}
74+
75+
@Override
76+
public void callFlutterEchoString(@NonNull String aString, Result<String> result) {
77+
flutterApi.echoString(
78+
aString,
79+
new FlutterIntegrationCoreApi.Reply<String>() {
80+
public void reply(String value) {
81+
result.success(value);
82+
}
83+
});
84+
}
4985
}

packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66

77
#import "CoreTests.gen.h"
88

9+
@interface AlternateLanguageTestPlugin ()
10+
@property(nonatomic) FlutterIntegrationCoreApi *flutterAPI;
11+
@end
12+
913
/**
10-
* This plugin is currently a no-op since only unit tests have been set up.
11-
* In the future, this will register Pigeon APIs used in integration tests.
14+
* This plugin handles the native side of the integration tests in example/integration_test/.
1215
*/
1316
@implementation AlternateLanguageTestPlugin
1417
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
1518
AlternateLanguageTestPlugin *plugin = [[AlternateLanguageTestPlugin alloc] init];
16-
HostIntegrationCoreApiSetup(registrar.messenger, plugin);
19+
HostIntegrationCoreApiSetup([registrar messenger], plugin);
20+
plugin.flutterAPI =
21+
[[FlutterIntegrationCoreApi alloc] initWithBinaryMessenger:[registrar messenger]];
1722
}
1823

1924
#pragma mark HostIntegrationCoreApi implementation
@@ -43,4 +48,27 @@ - (nullable AllTypesWrapper *)createNestedObjectWithString:(NSString *)string
4348
return [AllTypesWrapper makeWithValues:innerObject];
4449
}
4550

51+
- (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion {
52+
completion(nil);
53+
}
54+
55+
- (void)echoAsyncString:(NSString *)aString
56+
completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion {
57+
completion(aString, nil);
58+
}
59+
60+
- (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion {
61+
[self.flutterAPI noopWithCompletion:^(NSError *error) {
62+
completion(error);
63+
}];
64+
}
65+
66+
- (void)callFlutterEchoString:(NSString *)aString
67+
completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion {
68+
[self.flutterAPI echoString:aString
69+
completion:^(NSString *value, NSError *error) {
70+
completion(value, error);
71+
}];
72+
}
73+
4674
@end

packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ enum TargetGenerator {
3535
void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
3636
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
3737

38-
group('Host API tests', () {
38+
group('Host sync API tests', () {
3939
testWidgets('basic void->void call works', (WidgetTester _) async {
4040
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
4141

@@ -125,8 +125,65 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
125125
});
126126
});
127127

128-
group('Flutter API tests', () {
129-
// TODO(stuartmorgan): Add Flutter API tests, driven by wrapper host APIs
130-
// that forward the arguments and return values in the opposite direction.
128+
group('Host async API tests', () {
129+
testWidgets('basic void->void call works', (WidgetTester _) async {
130+
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
131+
132+
expect(api.noopAsync(), completes);
133+
});
134+
135+
testWidgets('strings serialize and deserialize correctly',
136+
(WidgetTester _) async {
137+
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
138+
139+
const String sentObject = 'Hello, asyncronously!';
140+
141+
final String echoObject = await api.echoAsyncString(sentObject);
142+
expect(echoObject, sentObject);
143+
});
131144
});
145+
146+
// These tests rely on the ansync Dart->host calls to work correctly, since
147+
// the host->Dart call is wrapped in a driving Dart->host call, so any test
148+
// added to this group should have coverage of the relevant arguments and
149+
// return value in the "Host async API tests" group.
150+
group('Flutter API tests', () {
151+
setUp(() {
152+
FlutterIntegrationCoreApi.setup(_FlutterApiTestImplementation());
153+
});
154+
155+
testWidgets('basic void->void call works', (WidgetTester _) async {
156+
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
157+
158+
expect(api.callFlutterNoop(), completes);
159+
});
160+
161+
testWidgets('strings serialize and deserialize correctly',
162+
(WidgetTester _) async {
163+
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
164+
165+
const String sentObject = 'Hello Dart!';
166+
167+
final String echoObject = await api.callFlutterEchoString(sentObject);
168+
expect(echoObject, sentObject);
169+
});
170+
},
171+
// TODO(stuartmorgan): Enable when FlutterApi generation is fixed for
172+
// C++. See https://github.com/flutter/flutter/issues/108682.
173+
skip: targetGenerator == TargetGenerator.cpp);
174+
}
175+
176+
class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi {
177+
@override
178+
AllTypes echoAllTypes(AllTypes everything) {
179+
return everything;
180+
}
181+
182+
@override
183+
String echoString(String aString) {
184+
return aString;
185+
}
186+
187+
@override
188+
void noop() {}
132189
}

0 commit comments

Comments
 (0)