diff --git a/.github/workflows/jnigen.yaml b/.github/workflows/jnigen.yaml index 22045c9659..6748a1f078 100644 --- a/.github/workflows/jnigen.yaml +++ b/.github/workflows/jnigen.yaml @@ -143,11 +143,14 @@ jobs: run: dart run jnigen:setup - name: Run VM tests run: | - if [ "${{ matrix.java-version }}" != "17" ]; then - dart test -x bindings --test-randomize-ordering-seed random - else - dart test --test-randomize-ordering-seed random - fi + for i in $(seq 1 10); do + echo "=== Iteration $i ===" + if [ "${{ matrix.java-version }}" != "17" ]; then + dart test -x bindings --test-randomize-ordering-seed random || exit 1 + else + dart test --test-randomize-ordering-seed random || exit 1 + fi + done - name: build in_app_java APK run: flutter build apk --target-platform=android-arm64 working-directory: ./pkgs/jnigen/example/in_app_java diff --git a/pkgs/jnigen/pubspec.yaml b/pkgs/jnigen/pubspec.yaml index 68312cc87e..8508aacf19 100644 --- a/pkgs/jnigen/pubspec.yaml +++ b/pkgs/jnigen/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: dev_dependencies: build_runner: ^2.4.12 dart_flutter_team_lints: ^3.5.2 + ffi: ^2.1.3 jni: path: ../jni json_serializable: ^6.8.0 diff --git a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart index f5f518de6f..cc036ded02 100644 --- a/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart +++ b/pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart @@ -719,6 +719,19 @@ void registerTests(String groupName, TestRunnerCallback test) { }); group('Interface implementation on destroyed isolate', () { + Future waitUntilCollected(MyConsumerRunner runner) async { + for (var i = 0; i < 8; ++i) { + if (i > 0) { + await Future.delayed( + Duration(milliseconds: (1 << i) * 100)); + } + await runBothGC(); + if (runner.isArgCollected) { + return; + } + } + } + test('non-blocking callback - successful callback flow', () async { final runnerPort = ReceivePort(); final callbackPort = ReceivePort(); @@ -740,12 +753,12 @@ void registerTests(String groupName, TestRunnerCallback test) { final result = await callbackPort.first; expect(result, 'callback_executed'); - expect(runner.isFinished, isTrue); + expect(runner.waitForFinished(1000), isTrue); isolate.kill(priority: Isolate.immediate); await exitPort.first; - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); @@ -773,14 +786,14 @@ void registerTests(String groupName, TestRunnerCallback test) { arg.release(); // Runner finished even though message isn't delivered. - expect(runner.isFinished, isTrue); + expect(runner.waitForFinished(1000), isTrue); expect( callbackPort.first.timeout(const Duration(milliseconds: 200)), throwsA(isA()), ); // Arg is cleaned up even though message isn't delivered. - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); @@ -817,7 +830,7 @@ void registerTests(String groupName, TestRunnerCallback test) { ); // Arg is cleaned up even though message isn't delivered. - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); @@ -847,7 +860,7 @@ void registerTests(String groupName, TestRunnerCallback test) { isolate.kill(priority: Isolate.immediate); await exitPort.first; - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); @@ -882,7 +895,7 @@ void registerTests(String groupName, TestRunnerCallback test) { ); // Arg is cleaned up even though message isn't delivered. - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); @@ -919,10 +932,10 @@ void registerTests(String groupName, TestRunnerCallback test) { ); // Arg is cleaned up even though message isn't delivered. - runJavaGC(); + await waitUntilCollected(runner); expect(runner.isArgCollected, isTrue); }); - }, skip: !canRunJavaGC); + }, skip: !canRunJavaGC || !canDoGC); } group('Dart exceptions are handled', () { diff --git a/pkgs/jnigen/test/test_util/java_gc.dart b/pkgs/jnigen/test/test_util/java_gc.dart index cdd6113af8..4d5fb47712 100644 --- a/pkgs/jnigen/test/test_util/java_gc.dart +++ b/pkgs/jnigen/test/test_util/java_gc.dart @@ -5,7 +5,7 @@ import 'dart:ffi'; import 'dart:io'; -import 'package:jni/_internal.dart'; +import 'package:ffi/ffi.dart'; import 'package:jni/jni.dart'; // Can't reliably force GC on Android. @@ -30,6 +30,34 @@ void runJavaGC() { } while (result.exitCode != 0); } +final _executeInternalCommand = () { + final dylib = DynamicLibrary.process(); + if (dylib.providesSymbol('Dart_ExecuteInternalCommand')) { + return dylib + .lookup, Pointer)>>( + 'Dart_ExecuteInternalCommand', + ) + .asFunction, Pointer)>(); + } + return null; +}(); + +final canDoGC = _executeInternalCommand != null; + +void runDartGC() { + if (_executeInternalCommand == null) return; + final gcNow = 'gc-now'.toNativeUtf8(); + _executeInternalCommand!(gcNow.cast(), nullptr); + calloc.free(gcNow); +} + +Future runBothGC() async { + runDartGC(); + await Future.delayed(Duration.zero); + runDartGC(); + runJavaGC(); +} + /// A wrapper around Java's `java.lang.ref.WeakReference` for testing GC /// collection. class JWeakReference {