| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // Tests on generated code. |
| // |
| // Both the simple java example & jackson core classes example have tests in |
| // same file, because the test runner will reuse the process, which leads to |
| // reuse of the old JVM with old classpath if we have separate tests with |
| // different classpaths. |
| |
| import 'dart:io'; |
| |
| import 'package:jni/jni.dart'; |
| import 'package:path/path.dart' hide equals; |
| import 'package:test/test.dart'; |
| |
| // ignore_for_file: avoid_relative_lib_imports |
| import 'kotlin_test/lib/kotlin.dart'; |
| import 'simple_package_test/lib/simple_package.dart'; |
| import 'jackson_core_test/third_party/lib/com/fasterxml/jackson/core/_package.dart'; |
| |
| import 'test_util/test_util.dart'; |
| |
| final simplePackageTest = join('test', 'simple_package_test'); |
| final jacksonCoreTest = join('test', 'jackson_core_test'); |
| final kotlinTest = join('test', 'kotlin_test'); |
| final jniJar = join(kotlinTest, 'jni.jar'); |
| |
| final simplePackageTestJava = join(simplePackageTest, 'java'); |
| final kotlinTestKotlin = join(kotlinTest, 'kotlin'); |
| |
| Future<void> setupDylibsAndClasses() async { |
| await runCommand('dart', [ |
| 'run', |
| 'jni:setup', |
| '-p', |
| 'jni', |
| '-s', |
| join(simplePackageTest, 'src'), |
| ]); |
| final group = join('com', 'github', 'dart_lang', 'jnigen'); |
| await runCommand( |
| 'javac', |
| [ |
| join(group, 'simple_package', 'Example.java'), |
| join(group, 'generics', 'MyMap.java'), |
| join(group, 'generics', 'MyStack.java'), |
| join(group, 'generics', 'GrandParent.java'), |
| join(group, 'generics', 'StringStack.java'), |
| join(group, 'generics', 'StringValuedMap.java'), |
| join(group, 'generics', 'StringKeyedMap.java'), |
| join(group, 'annotations', 'JsonSerializable.java'), |
| join(group, 'annotations', 'MyDataClass.java'), |
| join(group, 'pkg2', 'C2.java'), |
| join(group, 'pkg2', 'Example.java'), |
| ], |
| workingDirectory: simplePackageTestJava); |
| await runCommand('dart', [ |
| 'run', |
| 'jnigen:download_maven_jars', |
| '--config', |
| join(jacksonCoreTest, 'jnigen.yaml') |
| ]); |
| |
| final jacksonJars = await getJarPaths(join(jacksonCoreTest, 'third_party')); |
| |
| await runCommand('dart', [ |
| 'run', |
| 'jni:setup', |
| '-p', |
| 'jni', |
| '-s', |
| join(kotlinTest, 'src'), |
| ]); |
| await runCommand( |
| 'mvn', |
| ['package'], |
| workingDirectory: kotlinTestKotlin, |
| runInShell: true, |
| ); |
| // Jar including Kotlin runtime and dependencies. |
| final kotlinTestJar = |
| join(kotlinTestKotlin, 'target', 'kotlin_test-jar-with-dependencies.jar'); |
| |
| if (!Platform.isAndroid) { |
| Jni.spawn(dylibDir: join('build', 'jni_libs'), classPath: [ |
| jniJar, |
| simplePackageTestJava, |
| ...jacksonJars, |
| kotlinTestJar, |
| ]); |
| } |
| |
| Jni.initDLApi(); |
| } |
| |
| void main() async { |
| setUpAll(setupDylibsAndClasses); |
| |
| test('static final fields', () { |
| expect(Example.ON, equals(1)); |
| expect(Example.OFF, equals(0)); |
| }); |
| |
| test('static & instance fields', () { |
| expect(Example.num, equals(121)); |
| final aux = Example.aux; |
| expect(aux.value, equals(true)); |
| aux.delete(); |
| expect(C2.CONSTANT, equals(12)); |
| }); |
| |
| test('static methods', () { |
| expect(Example.addInts(10, 15), equals(25)); |
| }); |
| |
| test('static methods arrays', () { |
| final array = Example.getArr(); |
| expect(array[0], 1); |
| expect(array[1], 2); |
| expect(array[2], 3); |
| expect(Example.addAll(array), 6); |
| array[0] = 4; |
| expect(Example.addAll(array), 9); |
| }); |
| |
| test('instance methods', () { |
| final ex = Example(); |
| expect(ex.getNum(), equals(Example.num)); |
| final aux = Example.getAux(); |
| expect(aux.getValue(), equals(true)); |
| aux.setValue(false); |
| expect(aux.getValue(), equals(false)); |
| aux.setValue(true); |
| aux.delete(); |
| ex.delete(); |
| }); |
| |
| test('array of the class', () { |
| final ex1 = Example(); |
| final ex2 = Example(); |
| ex1.setInternal(1); |
| ex2.setInternal(2); |
| final array = JArray(Example.type, 2); |
| array[0] = ex1; |
| array[1] = ex2; |
| expect(array[0].getInternal(), 1); |
| expect(array[1].getInternal(), 2); |
| array.delete(); |
| ex1.delete(); |
| ex2.delete(); |
| }); |
| |
| test("Check bindings for same-named classes", () { |
| expect(Example().whichExample(), 0); |
| expect(Example1().whichExample(), 1); |
| }); |
| |
| test('simple json parsing test', () { |
| final json = JString.fromString('[1, true, false, 2, 4]'); |
| JsonFactory factory; |
| factory = JsonFactory(); |
| final parser = factory.createParser6(json); |
| final values = <bool>[]; |
| while (!parser.isClosed()) { |
| final next = parser.nextToken(); |
| if (next.isNull) continue; |
| values.add(next.isNumeric()); |
| next.delete(); |
| } |
| expect(values, equals([false, true, false, false, true, true, false])); |
| Jni.deleteAll([factory, parser, json]); |
| }); |
| test("parsing invalid JSON throws JniException", () { |
| using((arena) { |
| final factory = JsonFactory()..deletedIn(arena); |
| final erroneous = factory |
| .createParser6("<html>".toJString()..deletedIn(arena)) |
| ..deletedIn(arena); |
| expect(() => erroneous.nextToken(), throwsA(isA<JniException>())); |
| }); |
| }); |
| test('exceptions', () { |
| expect(() => Example.throwException(), throwsException); |
| }); |
| group('generics', () { |
| test('MyStack<T>', () { |
| using((arena) { |
| final stack = MyStack(JString.type)..deletedIn(arena); |
| stack.push('Hello'.toJString()..deletedIn(arena)); |
| stack.push('World'.toJString()..deletedIn(arena)); |
| expect(stack.pop().toDartString(deleteOriginal: true), 'World'); |
| expect(stack.pop().toDartString(deleteOriginal: true), 'Hello'); |
| }); |
| }); |
| test('MyMap<K, V>', () { |
| using((arena) { |
| final map = MyMap(JString.type, Example.type)..deletedIn(arena); |
| final helloExample = Example.ctor1(1)..deletedIn(arena); |
| final worldExample = Example.ctor1(2)..deletedIn(arena); |
| map.put('Hello'.toJString()..deletedIn(arena), helloExample); |
| map.put('World'.toJString()..deletedIn(arena), worldExample); |
| expect( |
| (map.get0('Hello'.toJString()..deletedIn(arena))..deletedIn(arena)) |
| .getInternal(), |
| 1, |
| ); |
| expect( |
| (map.get0('World'.toJString()..deletedIn(arena))..deletedIn(arena)) |
| .getInternal(), |
| 2, |
| ); |
| expect( |
| ((map.entryStack()..deletedIn(arena)).pop()..deletedIn(arena)) |
| .key |
| .castTo(JString.type, deleteOriginal: true) |
| .toDartString(deleteOriginal: true), |
| anyOf('Hello', 'World'), |
| ); |
| }); |
| }); |
| group('classes extending generics', () { |
| test('StringStack', () { |
| using((arena) { |
| final stringStack = StringStack()..deletedIn(arena); |
| stringStack.push('Hello'.toJString()..deletedIn(arena)); |
| expect(stringStack.pop().toDartString(deleteOriginal: true), 'Hello'); |
| }); |
| }); |
| test('StringKeyedMap', () { |
| using((arena) { |
| final map = StringKeyedMap(Example.type)..deletedIn(arena); |
| final example = Example()..deletedIn(arena); |
| map.put('Hello'.toJString()..deletedIn(arena), example); |
| expect( |
| (map.get0('Hello'.toJString()..deletedIn(arena))..deletedIn(arena)) |
| .getInternal(), |
| 0, |
| ); |
| }); |
| }); |
| test('StringValuedMap', () { |
| using((arena) { |
| final map = StringValuedMap(Example.type)..deletedIn(arena); |
| final example = Example()..deletedIn(arena); |
| map.put(example, 'Hello'.toJString()..deletedIn(arena)); |
| expect( |
| map.get0(example).toDartString(deleteOriginal: true), |
| 'Hello', |
| ); |
| }); |
| }); |
| }); |
| test('nested generics', () { |
| using((arena) { |
| final grandParent = |
| GrandParent(JString.type, "!".toJString()..deletedIn(arena)) |
| ..deletedIn(arena); |
| expect( |
| grandParent.value.toDartString(deleteOriginal: true), |
| "!", |
| ); |
| |
| final strStaticParent = GrandParent.stringStaticParent() |
| ..deletedIn(arena); |
| expect( |
| strStaticParent.value.toDartString(deleteOriginal: true), |
| "Hello", |
| ); |
| |
| final exampleStaticParent = GrandParent.varStaticParent( |
| Example.type, Example()..deletedIn(arena)) |
| ..deletedIn(arena); |
| expect( |
| (exampleStaticParent.value..deletedIn(arena)).getInternal(), |
| 0, |
| ); |
| |
| final strParent = grandParent.stringParent()..deletedIn(arena); |
| expect( |
| strParent.parentValue |
| .castTo(JString.type, deleteOriginal: true) |
| .toDartString(deleteOriginal: true), |
| "!", |
| ); |
| expect( |
| strParent.value.toDartString(deleteOriginal: true), |
| "Hello", |
| ); |
| |
| final exampleParent = grandParent.varParent( |
| Example.type, Example()..deletedIn(arena)) |
| ..deletedIn(arena); |
| expect( |
| exampleParent.parentValue |
| .castTo(JString.type, deleteOriginal: true) |
| .toDartString(deleteOriginal: true), |
| "!", |
| ); |
| expect( |
| (exampleParent.value..deletedIn(arena)).getInternal(), |
| 0, |
| ); |
| // TODO(#139): test constructing Child, currently does not work due |
| // to a problem with C-bindings. |
| }); |
| }); |
| }); |
| group('Kotlin support', () { |
| test('Suspend functions', () async { |
| await using((arena) async { |
| final suspendFun = SuspendFun()..deletedIn(arena); |
| final hello = await suspendFun.sayHello(); |
| expect(hello.toDartString(deleteOriginal: true), "Hello!"); |
| const name = "Bob"; |
| final helloBob = |
| await suspendFun.sayHello1(name.toJString()..deletedIn(arena)); |
| expect(helloBob.toDartString(deleteOriginal: true), "Hello $name!"); |
| }); |
| }); |
| }); |
| } |