Skip to content

Commit e7c3837

Browse files
[pigeon] Add local-only Kotlin integration tests (#2852)
* [pigeon] Add local-only Kotlin integration tests Adds a new test target to run the new shared integration tests on Kotlin, and adds the necessary native-side plumbing. This is not run by default because it requires a device or emulator to be present, and we don't have that set up for CI yet (and we don't yet have a different script entrypoint for manual vs CI tests). Enabling this in CI is tracked in flutter/flutter#111505, but landing it now allows for better manual testing of generator changes during development. Part of flutter/flutter#111505 * Add copyright
1 parent 33bf242 commit e7c3837

File tree

5 files changed

+101
-8
lines changed

5 files changed

+101
-8
lines changed

packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,32 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
1313
import io.flutter.plugin.common.MethodChannel.Result
1414

1515
/**
16-
* This plugin is currently a no-op since only unit tests have been set up.
17-
* In the future, this will register Pigeon APIs used in integration tests.
16+
* This plugin handles the native side of the integration tests in
17+
* example/integration_test/.
1818
*/
19-
class TestPlugin: FlutterPlugin {
20-
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
19+
class TestPlugin: FlutterPlugin, AllVoidHostApi, HostEverything {
20+
override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
21+
AllVoidHostApi.setUp(binding.getBinaryMessenger(), this)
22+
HostEverything.setUp(binding.getBinaryMessenger(), this)
2123
}
2224

2325
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
2426
}
27+
28+
// AllVoidHostApi
29+
30+
override fun doit() {
31+
// No-op.
32+
}
33+
34+
// HostEverything
35+
36+
override fun giveMeEverything(): Everything {
37+
// Currently unused in integration tests, so just return an empty object.
38+
return Everything()
39+
}
40+
41+
override fun echo(everything: Everything): Everything {
42+
return everything
43+
}
2544
}

packages/pigeon/run_tests.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ run_android_kotlin_unittests() {
177177
dart run tool/run_tests.dart -t android_kotlin_unittests --skip-generation
178178
}
179179

180+
run_android_kotlin_e2e_tests() {
181+
dart run tool/run_tests.dart -t android_kotlin_integration_tests --skip-generation
182+
}
183+
180184
run_dart_compilation_tests() {
181185
local temp_dir=$(mktmpdir)
182186
local flutter_project_dir=$temp_dir/project
@@ -261,6 +265,9 @@ should_run_mock_handler_tests=true
261265
should_run_macos_swift_unittests=true
262266
should_run_macos_swift_e2e_tests=true
263267
should_run_android_kotlin_unittests=true
268+
# Default to false until there is CI support. See
269+
# https://github.com/flutter/flutter/issues/111505
270+
should_run_android_kotlin_e2e_tests=false
264271
while getopts "t:l?h" opt; do
265272
case $opt in
266273
t)
@@ -275,6 +282,7 @@ while getopts "t:l?h" opt; do
275282
should_run_macos_swift_unittests=false
276283
should_run_macos_swift_e2e_tests=false
277284
should_run_android_kotlin_unittests=false
285+
should_run_android_kotlin_e2e_tests=false
278286
case $OPTARG in
279287
# TODO(stuartmorgan): Rename to include "java".
280288
android_unittests) should_run_android_unittests=true ;;
@@ -289,6 +297,7 @@ while getopts "t:l?h" opt; do
289297
macos_swift_unittests) should_run_macos_swift_unittests=true ;;
290298
macos_swift_e2e_tests) should_run_macos_swift_e2e_tests=true ;;
291299
android_kotlin_unittests) should_run_android_kotlin_unittests=true ;;
300+
android_kotlin_e2e_tests) should_run_android_kotlin_e2e_tests=true ;;
292301
*)
293302
echo "unrecognized test: $OPTARG"
294303
exit 1
@@ -299,6 +308,7 @@ while getopts "t:l?h" opt; do
299308
echo "available tests for -t:
300309
android_unittests - Unit tests on generated Java code.
301310
android_kotlin_unittests - Unit tests on generated Kotlin code on Android.
311+
android_kotlin_e2e_tests - Integration tests on generated Kotlin code on Android.
302312
dart_compilation_tests - Compilation tests on generated Dart code.
303313
dart_unittests - Unit tests on and analysis on Pigeon's implementation.
304314
flutter_unittests - Unit tests on generated Dart code.
@@ -370,3 +380,6 @@ fi
370380
if [ "$should_run_android_kotlin_unittests" = true ]; then
371381
run_android_kotlin_unittests
372382
fi
383+
if [ "$should_run_android_kotlin_e2e_tests" = true ]; then
384+
run_android_kotlin_e2e_tests
385+
fi

packages/pigeon/tool/run_tests.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'package:args/args.dart';
1616
import 'package:meta/meta.dart';
1717
import 'package:path/path.dart' as p;
1818

19+
import 'shared/flutter_utils.dart';
1920
import 'shared/generation.dart';
2021
import 'shared/native_project_runners.dart';
2122
import 'shared/process_utils.dart';
@@ -24,6 +25,8 @@ const String _testFlag = 'test';
2425
const String _listFlag = 'list';
2526
const String _skipGenerationFlag = 'skip-generation';
2627

28+
const int _noDeviceAvailableExitCode = 100;
29+
2730
const String _testPluginRelativePath = 'platform_tests/test_plugin';
2831
const String _integrationTestFileRelativePath = 'integration_test/test.dart';
2932

@@ -47,6 +50,9 @@ const Map<String, _TestInfo> _tests = <String, _TestInfo>{
4750
'android_kotlin_unittests': _TestInfo(
4851
function: _runAndroidKotlinUnitTests,
4952
description: 'Unit tests on generated Kotlin code.'),
53+
'android_kotlin_integration_tests': _TestInfo(
54+
function: _runAndroidKotlinIntegrationTests,
55+
description: 'Integration tests on generated Kotlin code.'),
5056
'dart_compilation_tests': _TestInfo(
5157
function: _runDartCompilationTests,
5258
description: 'Compilation tests on generated Dart code.'),
@@ -91,6 +97,22 @@ Future<int> _runAndroidKotlinUnitTests() async {
9197
return runGradleBuild(androidProjectPath, 'testDebugUnitTest');
9298
}
9399

100+
Future<int> _runAndroidKotlinIntegrationTests() async {
101+
final String? device = await getDeviceForPlatform('android');
102+
if (device == null) {
103+
print('No Android device available. Attach an Android device or start '
104+
'an emulator to run integration tests');
105+
return _noDeviceAvailableExitCode;
106+
}
107+
108+
const String examplePath = './$_testPluginRelativePath/example';
109+
return runFlutterCommand(
110+
examplePath,
111+
'test',
112+
<String>[_integrationTestFileRelativePath, '-d', device],
113+
);
114+
}
115+
94116
Future<int> _runDartCompilationTests() async {
95117
throw UnimplementedError('See run_tests.sh.');
96118
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
import 'dart:io';
7+
8+
String getFlutterCommand() => Platform.isWindows ? 'flutter.bat' : 'flutter';
9+
10+
/// Returns the first device listed by `flutter devices` that targets
11+
/// [platform], or null if there is no such device.
12+
Future<String?> getDeviceForPlatform(String platform) async {
13+
final ProcessResult result = await Process.run(
14+
getFlutterCommand(),
15+
<String>['devices', '--machine'],
16+
stdoutEncoding: utf8,
17+
);
18+
if (result.exitCode != 0) {
19+
return null;
20+
}
21+
22+
String output = result.stdout as String;
23+
// --machine doesn't currently prevent the tool from printing banners;
24+
// see https://github.com/flutter/flutter/issues/86055. This workaround
25+
// can be removed once that is fixed.
26+
output = output.substring(output.indexOf('['));
27+
28+
final List<Map<String, dynamic>> devices =
29+
(jsonDecode(output) as List<dynamic>).cast<Map<String, dynamic>>();
30+
for (final Map<String, dynamic> deviceInfo in devices) {
31+
final String targetPlatform =
32+
(deviceInfo['targetPlatform'] as String?) ?? '';
33+
if (targetPlatform.startsWith(platform)) {
34+
final String? deviceId = deviceInfo['id'] as String?;
35+
if (deviceId != null) {
36+
return deviceId;
37+
}
38+
}
39+
}
40+
return null;
41+
}

packages/pigeon/tool/shared/native_project_runners.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:io';
6-
5+
import 'flutter_utils.dart';
76
import 'process_utils.dart';
87

98
Future<int> runFlutterCommand(
109
String projectDirectory,
1110
String command, [
1211
List<String> commandArguments = const <String>[],
1312
]) {
14-
final String flutterCommand = Platform.isWindows ? 'flutter.bat' : 'flutter';
1513
return runProcess(
16-
flutterCommand,
14+
getFlutterCommand(),
1715
<String>[
1816
command,
1917
...commandArguments,

0 commit comments

Comments
 (0)