Skip to content
Draft
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
13 changes: 8 additions & 5 deletions .github/workflows/jnigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkgs/jnigen/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 22 additions & 9 deletions pkgs/jnigen/test/simple_package_test/runtime_test_registrant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,19 @@ void registerTests(String groupName, TestRunnerCallback test) {
});

group('Interface implementation on destroyed isolate', () {
Future<void> waitUntilCollected(MyConsumerRunner runner) async {
for (var i = 0; i < 8; ++i) {
if (i > 0) {
await Future<void>.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();
Expand All @@ -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);
});

Expand Down Expand Up @@ -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<TimeoutException>()),
);

// Arg is cleaned up even though message isn't delivered.
runJavaGC();
await waitUntilCollected(runner);
expect(runner.isArgCollected, isTrue);
});

Expand Down Expand Up @@ -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);
});

Expand Down Expand Up @@ -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);
});

Expand Down Expand Up @@ -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);
});

Expand Down Expand Up @@ -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', () {
Expand Down
30 changes: 29 additions & 1 deletion pkgs/jnigen/test/test_util/java_gc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -30,6 +30,34 @@ void runJavaGC() {
} while (result.exitCode != 0);
}

final _executeInternalCommand = () {
final dylib = DynamicLibrary.process();
if (dylib.providesSymbol('Dart_ExecuteInternalCommand')) {
return dylib
.lookup<NativeFunction<Void Function(Pointer<Char>, Pointer<Void>)>>(
'Dart_ExecuteInternalCommand',
)
.asFunction<void Function(Pointer<Char>, Pointer<Void>)>();
}
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<void> runBothGC() async {
runDartGC();
await Future<void>.delayed(Duration.zero);
runDartGC();
runJavaGC();
}

/// A wrapper around Java's `java.lang.ref.WeakReference` for testing GC
/// collection.
class JWeakReference {
Expand Down
Loading