From ab6260ee461aa7e4363a993794c0956116a0ad9c Mon Sep 17 00:00:00 2001 From: Littlegnal <8847263+littleGnAl@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:16:57 +0800 Subject: [PATCH] fix: [native] Fix errors when hot restarted (#86) * fix: [native] Fix errors when hot restarted --- .github/workflows/ci.yaml | 13 ++- .../iris_event_smoke_test.dart | 25 +++-- .../iris_method_channel_smoke_test.dart | 94 +++++++++++++++++++ example/test/widget_test.dart | 4 +- .../io/iris_method_channel_internal_io.dart | 12 ++- lib/src/platform/utils_expect.dart | 2 +- test/platform/platform_cases.dart | 2 +- 7 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 example/integration_test/iris_method_channel_smoke_test.dart diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bb457db..3c3cd3e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -229,9 +229,14 @@ jobs: flutter-version: '3.10.0' # Run the latest version cache: true - name: windows integration test + shell: bash run: | flutter packages get - flutter test integration_test -d windows + + # It's a little tricky that you should run integration test one by one on flutter macOS/Windows + for filename in integration_test/*.dart; do + flutter test $filename -d windows + done working-directory: example integration_test_ios: @@ -274,7 +279,11 @@ jobs: - name: run macos integration test run: | flutter packages get - flutter test integration_test/iris_event_smoke_test.dart -d macos + + # It's a little tricky that you should run integration test one by one on flutter macOS/Windows + for filename in integration_test/*.dart; do + flutter test $filename -d macos + done working-directory: example integration_test_android: diff --git a/example/integration_test/iris_event_smoke_test.dart b/example/integration_test/iris_event_smoke_test.dart index 47038e0..3acafb2 100644 --- a/example/integration_test/iris_event_smoke_test.dart +++ b/example/integration_test/iris_event_smoke_test.dart @@ -1,5 +1,6 @@ import 'dart:isolate'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:iris_method_channel/src/platform/io/iris_event_io.dart'; @@ -7,15 +8,19 @@ import 'package:iris_method_channel/src/platform/io/iris_event_io.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('IrisEvent smoke test', (tester) async { - await tester.pumpAndSettle(); + testWidgets( + 'IrisEvent smoke test', + (tester) async { + await tester.pumpWidget(Container()); - IrisEventIO irisEvent = IrisEventIO(); - irisEvent.initialize(); - final testPort = ReceivePort(); - irisEvent.registerEventHandler(testPort.sendPort); - irisEvent.unregisterEventHandler(testPort.sendPort); - irisEvent.onEventPtr; - irisEvent.dispose(); - }); + IrisEventIO irisEvent = IrisEventIO(); + irisEvent.initialize(); + final testPort = ReceivePort(); + irisEvent.registerEventHandler(testPort.sendPort); + irisEvent.unregisterEventHandler(testPort.sendPort); + irisEvent.onEventPtr; + irisEvent.dispose(); + }, + timeout: const Timeout(Duration(minutes: 10)), + ); } diff --git a/example/integration_test/iris_method_channel_smoke_test.dart b/example/integration_test/iris_method_channel_smoke_test.dart new file mode 100644 index 0000000..f585c79 --- /dev/null +++ b/example/integration_test/iris_method_channel_smoke_test.dart @@ -0,0 +1,94 @@ +import 'dart:isolate'; +import 'dart:ffi' as ffi; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:iris_method_channel/iris_method_channel.dart'; +import 'package:iris_method_channel/src/platform/io/iris_method_channel_internal_io.dart'; + +class _FakeNativeBindingDelegate extends PlatformBindingsDelegateInterface { + _FakeNativeBindingDelegate(); + + @override + int callApi( + IrisMethodCall methodCall, + IrisApiEngineHandle apiEnginePtr, + IrisApiParamHandle param, + ) { + return 0; + } + + @override + IrisEventHandlerHandle createIrisEventHandler( + IrisCEventHandlerHandle eventHandler, + ) { + return IrisEventHandlerHandle(ffi.Pointer.fromAddress(123456)); + } + + @override + CreateApiEngineResult createApiEngine(List args) { + return CreateApiEngineResult( + IrisApiEngineHandle(ffi.Pointer.fromAddress(100)), + extraData: {'extra_handle': 1000}, + ); + } + + @override + void destroyIrisEventHandler( + IrisEventHandlerHandle handler, + ) {} + + @override + void destroyNativeApiEngine(IrisApiEngineHandle apiEnginePtr) {} + + @override + void initialize() {} + + @override + Future callApiAsync(IrisMethodCall methodCall, + IrisApiEngineHandle apiEnginePtr, IrisApiParamHandle param) async { + return CallApiResult(irisReturnCode: 0, data: {}); + } +} + +class _FakeNativeBindingDelegateProvider extends PlatformBindingsProvider { + _FakeNativeBindingDelegateProvider(this.nativeBindingDelegate); + + final PlatformBindingsDelegateInterface nativeBindingDelegate; + + @override + PlatformBindingsDelegateInterface provideNativeBindingDelegate() { + return nativeBindingDelegate; + } +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets( + 'IrisMethodChannel should call hot restart listener', + (tester) async { + await tester.pumpWidget(Container()); + + final irisMethodChannel = IrisMethodChannel( + _FakeNativeBindingDelegateProvider(_FakeNativeBindingDelegate())); + await irisMethodChannel.initilize([]); + + bool hotRestartListenerCalled = false; + irisMethodChannel.addHotRestartListener((message) { + hotRestartListenerCalled = true; + }); + + (irisMethodChannel.getIrisMethodChannelInternal() + as IrisMethodChannelInternalIO) + .workerIsolate + .kill(priority: Isolate.immediate); + // Delayed 2 seconds to ensure `irisMethodChannel.workerIsolate.kill` done + await Future.delayed(const Duration(seconds: 2)); + + expect(hotRestartListenerCalled, true); + }, + timeout: const Timeout(Duration(minutes: 10)), + ); +} diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 779dad8..f7eac13 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -18,8 +18,8 @@ void main() { // Verify that platform version is retrieved. expect( find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/lib/src/platform/io/iris_method_channel_internal_io.dart b/lib/src/platform/io/iris_method_channel_internal_io.dart index bf7fa3b..940025e 100644 --- a/lib/src/platform/io/iris_method_channel_internal_io.dart +++ b/lib/src/platform/io/iris_method_channel_internal_io.dart @@ -150,14 +150,16 @@ class _HotRestartFinalizer { nativeBindingDelegate.initialize(); nativeBindingDelegate.destroyNativeApiEngine(IrisApiEngineHandle( - ffi.Pointer.fromAddress(_debugIrisApiEngineNativeHandle!))); + ffi.Pointer.fromAddress(_debugIrisApiEngineNativeHandle!))); - calloc.free(ffi.Pointer.fromAddress(_debugIrisCEventHandlerNativeHandle!)); + calloc.free(ffi.Pointer.fromAddress( + _debugIrisCEventHandlerNativeHandle!)); nativeBindingDelegate.destroyIrisEventHandler(IrisEventHandlerHandle( - ffi.Pointer.fromAddress(_debugIrisEventHandlerNativeHandle!))); + ffi.Pointer.fromAddress( + _debugIrisEventHandlerNativeHandle!))); - assert(provider.provideIrisEvent() != null); - final irisEvent = provider.provideIrisEvent()! as IrisEventIO; + final irisEvent = + (provider.provideIrisEvent() ?? IrisEventIO()) as IrisEventIO; irisEvent.dispose(); _onExitSubscription?.cancel(); diff --git a/lib/src/platform/utils_expect.dart b/lib/src/platform/utils_expect.dart index 11bfd27..5acdb03 100644 --- a/lib/src/platform/utils_expect.dart +++ b/lib/src/platform/utils_expect.dart @@ -1,6 +1,6 @@ import 'dart:typed_data'; -/// Stub function for translate the int ptr to the [Uint8List]. +/// Stub function for translate the int ptr to the [Uint8List]. /// See implementation: /// io: `io/utils_actual_io.dart` /// web: `web/utils_actual_web.dart`(Empty implementation) diff --git a/test/platform/platform_cases.dart b/test/platform/platform_cases.dart index 519d0e2..d56a267 100644 --- a/test/platform/platform_cases.dart +++ b/test/platform/platform_cases.dart @@ -1,3 +1,3 @@ export 'platform_cases_expect.dart' - if (dart.library.io) 'platform_cases_actual_io.dart' + if (dart.library.io) 'platform_cases_actual_io.dart' if (dart.library.html) 'platform_cases_actual_web.dart';