blob: 47c8c96f7f429f84626f9dc9b0682a454bfbc221 [file] [log] [blame] [edit]
import 'dart:io';
import 'dart:isolate';
import 'package:jni/jni.dart';
import 'package:test/test.dart';
import 'test_util/test_util.dart';
void main() {
if (!Platform.isAndroid) {
checkDylibIsUpToDate();
spawnJvm();
}
test('Debug mode captures stack trace on double release', () {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
final s = 'hello'.toJString();
s.release();
try {
s.release();
// ignore: avoid_catching_errors
} on DoubleReleaseError catch (e) {
expect(e.toString(), contains('Object was released at:'));
expect(e.toString(), contains('debug_release_test.dart'));
}
});
test('Debug mode captures stack trace on use after release', () {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
final s = 'hello'.toJString();
s.release();
try {
s.toDartString();
// ignore: avoid_catching_errors
} on UseAfterReleaseError catch (e) {
expect(e.toString(), contains('Object was released at:'));
expect(e.toString(), contains('debug_release_test.dart'));
}
});
test('Debug mode captures arena stack trace', () {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
final s = 'hello'.toJString();
s.release();
using(s.releasedBy);
using(s.releasedBy);
try {
s.toDartString();
// ignore: avoid_catching_errors
} on UseAfterReleaseError catch (e) {
expect(
e.toString(),
stringContainsInOrder([
'Object was released at:',
'Object was registered to be released',
'Object was registered to be released'
]));
expect(e.toString(), contains('debug_release_test.dart'));
}
try {
s.release();
// ignore: avoid_catching_errors
} on DoubleReleaseError catch (e) {
expect(
e.toString(),
stringContainsInOrder([
'Object was released at:',
'Object was registered to be released',
'Object was registered to be released'
]));
expect(e.toString(), contains('debug_release_test.dart'));
}
});
test('Hint is shown when debug mode is disabled', () {
Jni.captureStackTraceOnRelease = false;
final s = 'hello'.toJString();
s.release();
try {
s.release();
// ignore: avoid_catching_errors
} on DoubleReleaseError catch (e) {
expect(e.toString(),
contains('set `Jni.captureStackTraceOnRelease = true`'));
}
});
test('Debug mode flag is shared across isolates', () async {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
final isEnabled = await Isolate.run(() {
return Jni.captureStackTraceOnRelease;
});
expect(isEnabled, isTrue);
Jni.captureStackTraceOnRelease = false;
final isEnabled2 = await Isolate.run(() {
return Jni.captureStackTraceOnRelease;
});
expect(isEnabled2, isFalse);
});
test('Debug mode captures stack trace in another isolate', () async {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
await Isolate.run(() {
final s = 'hello'.toJString();
s.release();
try {
s.release();
// ignore: avoid_catching_errors
} on DoubleReleaseError catch (e) {
if (!e.toString().contains('Object was released at:')) {
throw StateError('Stack trace not captured in isolate');
}
}
});
});
test(
'Debug mode captures stack trace from another isolate'
' (cross-isolate release)', () async {
Jni.captureStackTraceOnRelease = true;
addTearDown(() {
Jni.captureStackTraceOnRelease = false;
});
final s = 'hello'.toJString();
s.release();
final (isReleased, doubleReleaseError, useAfterReleaseError) =
await Isolate.run(() {
String? doubleReleaseError;
String? useAfterReleaseError;
try {
s.release();
// ignore: avoid_catching_errors
} on DoubleReleaseError catch (e) {
doubleReleaseError = e.toString();
}
try {
s.toString();
// ignore: avoid_catching_errors
} on UseAfterReleaseError catch (e) {
useAfterReleaseError = e.toString();
}
return (s.isReleased, doubleReleaseError, useAfterReleaseError);
});
expect(isReleased, isTrue);
expect(doubleReleaseError, contains('Object was released at:'));
expect(useAfterReleaseError, contains('Object was released at:'));
});
}