blob: ea6393bacf791fc9b9dadb995bcea1b9673919b6 [file] [log] [blame]
// Copyright (c) 2020, 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.
import 'dart:io';
import 'package:ffigen/src/code_generator.dart';
import 'package:test/test.dart';
void main() {
group('code_generator: ', () {
test('Function Binding (primitives, pointers)', () {
final library = Library(
name: 'Bindings',
bindings: [
Func(
name: 'noParam',
dartDoc: 'Just a test function\nheres another line',
returnType: Type.nativeType(
SupportedNativeType.Int32,
),
),
Func(
name: 'withPrimitiveParam',
parameters: [
Parameter(
name: 'a',
type: Type.nativeType(
SupportedNativeType.Int32,
),
),
Parameter(
name: 'b',
type: Type.nativeType(
SupportedNativeType.Uint8,
),
),
],
returnType: Type.nativeType(
SupportedNativeType.Char,
),
),
Func(
name: 'withPointerParam',
parameters: [
Parameter(
name: 'a',
type: Type.pointer(
Type.nativeType(
SupportedNativeType.Int32,
),
),
),
Parameter(
name: 'b',
type: Type.pointer(
Type.pointer(
Type.nativeType(
SupportedNativeType.Uint8,
),
),
),
),
],
returnType: Type.pointer(
Type.nativeType(
SupportedNativeType.Double,
),
),
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/Function-Binding-test-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class Bindings{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
/// Just a test function
/// heres another line
int noParam(
) {
return (_noParam ??= _dylib.lookupFunction<_c_noParam,_dart_noParam>('noParam'))(
);
}
_dart_noParam? _noParam;
int withPrimitiveParam(
int a,
int b,
) {
return (_withPrimitiveParam ??= _dylib.lookupFunction<_c_withPrimitiveParam,_dart_withPrimitiveParam>('withPrimitiveParam'))(
a,
b,
);
}
_dart_withPrimitiveParam? _withPrimitiveParam;
ffi.Pointer<ffi.Double> withPointerParam(
ffi.Pointer<ffi.Int32> a,
ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
) {
return (_withPointerParam ??= _dylib.lookupFunction<_c_withPointerParam,_dart_withPointerParam>('withPointerParam'))(
a,
b,
);
}
_dart_withPointerParam? _withPointerParam;
}
typedef _c_noParam = ffi.Int32 Function(
);
typedef _dart_noParam = int Function(
);
typedef _c_withPrimitiveParam = ffi.Uint8 Function(
ffi.Int32 a,
ffi.Uint8 b,
);
typedef _dart_withPrimitiveParam = int Function(
int a,
int b,
);
typedef _c_withPointerParam = ffi.Pointer<ffi.Double> Function(
ffi.Pointer<ffi.Int32> a,
ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
);
typedef _dart_withPointerParam = ffi.Pointer<ffi.Double> Function(
ffi.Pointer<ffi.Int32> a,
ffi.Pointer<ffi.Pointer<ffi.Uint8>> b,
);
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('Struct Binding (primitives, pointers)', () {
final library = Library(
name: 'Bindings',
bindings: [
Struc(
name: 'NoMember',
dartDoc: 'Just a test struct\nheres another line',
),
Struc(
name: 'WithPrimitiveMember',
members: [
Member(
name: 'a',
type: Type.nativeType(
SupportedNativeType.Int32,
),
),
Member(
name: 'b',
type: Type.nativeType(
SupportedNativeType.Double,
),
),
Member(
name: 'c',
type: Type.nativeType(
SupportedNativeType.Char,
),
),
],
),
Struc(
name: 'WithPointerMember',
members: [
Member(
name: 'a',
type: Type.pointer(
Type.nativeType(
SupportedNativeType.Int32,
),
),
),
Member(
name: 'b',
type: Type.pointer(
Type.pointer(
Type.nativeType(
SupportedNativeType.Double,
),
),
),
),
Member(
name: 'c',
type: Type.nativeType(
SupportedNativeType.Char,
),
),
],
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File('test/debug_generated/Struct-Binding-test-output.dart');
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Just a test struct
/// heres another line
class NoMember extends ffi.Struct{
}
class WithPrimitiveMember extends ffi.Struct{
@ffi.Int32()
external int a;
@ffi.Double()
external double b;
@ffi.Uint8()
external int c;
}
class WithPointerMember extends ffi.Struct{
external ffi.Pointer<ffi.Int32> a;
external ffi.Pointer<ffi.Pointer<ffi.Double>> b;
@ffi.Uint8()
external int c;
}
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('Function and Struct Binding (pointer to Struct)', () {
final struct_some = Struc(
name: 'SomeStruc',
members: [
Member(
name: 'a',
type: Type.nativeType(
SupportedNativeType.Int32,
),
),
Member(
name: 'b',
type: Type.nativeType(
SupportedNativeType.Double,
),
),
Member(
name: 'c',
type: Type.nativeType(
SupportedNativeType.Char,
),
),
],
);
final library = Library(
name: 'Bindings',
bindings: [
struct_some,
Func(
name: 'someFunc',
parameters: [
Parameter(
name: 'some',
type: Type.pointer(
Type.pointer(
Type.struct(
struct_some,
),
),
),
),
],
returnType: Type.pointer(
Type.struct(
struct_some,
),
),
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file =
File('test/debug_generated/Func-n-Struct-Binding-test-output.dart');
try {
//expect
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class Bindings{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
ffi.Pointer<SomeStruc> someFunc(
ffi.Pointer<ffi.Pointer<SomeStruc>> some,
) {
return (_someFunc ??= _dylib.lookupFunction<_c_someFunc,_dart_someFunc>('someFunc'))(
some,
);
}
_dart_someFunc? _someFunc;
}
class SomeStruc extends ffi.Struct{
@ffi.Int32()
external int a;
@ffi.Double()
external double b;
@ffi.Uint8()
external int c;
}
typedef _c_someFunc = ffi.Pointer<SomeStruc> Function(
ffi.Pointer<ffi.Pointer<SomeStruc>> some,
);
typedef _dart_someFunc = ffi.Pointer<SomeStruc> Function(
ffi.Pointer<ffi.Pointer<SomeStruc>> some,
);
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('global (primitives, pointers, pointer to struct)', () {
final struc_some = Struc(
name: 'Some',
);
final library = Library(
name: 'Bindings',
bindings: [
Global(
name: 'test1',
type: Type.nativeType(
SupportedNativeType.Int32,
),
),
Global(
name: 'test2',
type: Type.pointer(
Type.nativeType(
SupportedNativeType.Float,
),
),
),
struc_some,
Global(
name: 'test5',
type: Type.pointer(
Type.struct(
struc_some,
),
),
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/Global-Binding-test-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class Bindings{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
ffi.Pointer<ffi.Int32> _test1;
int get test1 => (_test1 ??= _dylib.lookup<ffi.Int32>('test1')).value;
ffi.Pointer<ffi.Pointer<ffi.Float>> _test2;
ffi.Pointer<ffi.Float> get test2 => (_test2 ??= _dylib.lookup<ffi.Pointer<ffi.Float>>('test2')).value;
ffi.Pointer<ffi.Pointer<Some>> _test5;
ffi.Pointer<Some> get test5 => (_test5 ??= _dylib.lookup<ffi.Pointer<Some>>('test5')).value;
}
class Some extends ffi.Struct{
}
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('constant', () {
final library = Library(
name: 'Bindings',
bindings: [
Constant(
name: 'test1',
rawType: 'int',
rawValue: '20',
),
Constant(
name: 'test2',
rawType: 'double',
rawValue: '20.0',
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/Constant-test-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
const int test1 = 20;
const double test2 = 20.0;
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('enum_class', () {
final library = Library(
name: 'Bindings',
bindings: [
EnumClass(
name: 'Constants',
dartDoc: 'test line 1\ntest line 2',
enumConstants: [
EnumConstant(
name: 'a',
value: 10,
),
EnumConstant(name: 'b', value: -1, dartDoc: 'negative'),
],
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/enum-class-test-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// test line 1
/// test line 2
abstract class Constants {
static const int a = 10;
/// negative
static const int b = -1;
}
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('Internal conflict resolution', () {
final library = Library(
name: 'init_dylib',
bindings: [
Func(
name: 'test',
returnType: Type.nativeType(SupportedNativeType.Void),
),
Func(
name: '_test',
returnType: Type.nativeType(SupportedNativeType.Void),
),
Func(
name: '_c_test',
returnType: Type.nativeType(SupportedNativeType.Void),
),
Func(
name: '_dart_test',
returnType: Type.nativeType(SupportedNativeType.Void),
),
Struc(
name: '_Test',
members: [
Member(
name: 'array',
type: Type.constantArray(
2,
Type.nativeType(
SupportedNativeType.Int8,
),
),
),
],
),
Struc(name: 'ArrayHelperPrefixCollisionTest'),
Func(
name: 'Test',
returnType: Type.nativeType(SupportedNativeType.Void),
),
EnumClass(name: '_c_Test'),
EnumClass(name: 'init_dylib'),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/internal-conflict-resolution.dart',
);
try {
expect(gen, r'''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class init_dylib_1{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
init_dylib_1(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
void test(
) {
return (_test_1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test'))(
);
}
_dart_test1? _test_1;
void _test(
) {
return (__test ??= _dylib.lookupFunction<_c__test,_dart__test>('_test'))(
);
}
_dart__test? __test;
void _c_test(
) {
return (__c_test ??= _dylib.lookupFunction<_c__c_test,_dart__c_test>('_c_test'))(
);
}
_dart__c_test? __c_test;
void _dart_test(
) {
return (__dart_test ??= _dylib.lookupFunction<_c__dart_test,_dart__dart_test>('_dart_test'))(
);
}
_dart__dart_test? __dart_test;
void Test(
) {
return (_Test ??= _dylib.lookupFunction<_c_Test1,_dart_Test>('Test'))(
);
}
_dart_Test? _Test;
}
class _Test extends ffi.Struct{
@ffi.Int8()
external int _unique_array_item_0;
@ffi.Int8()
external int _unique_array_item_1;
/// Helper for array `array`.
ArrayHelper1__Test_array_level0 get array => ArrayHelper1__Test_array_level0(this, [2], 0, 0);
}
/// Helper for array `array` in struct `_Test`.
class ArrayHelper1__Test_array_level0{
final _Test _struct;
final List<int> dimensions;
final int level;
final int _absoluteIndex;
int get length => dimensions[level];
ArrayHelper1__Test_array_level0(this._struct, this.dimensions, this.level, this._absoluteIndex);
void _checkBounds(int index) {
if (index >= length || index < 0) {
throw RangeError('Dimension $level: index not in range 0..${length} exclusive.');
}
}
int operator[](int index){
_checkBounds(index);
switch(_absoluteIndex+index){
case 0:
return _struct._unique_array_item_0;
case 1:
return _struct._unique_array_item_1;
default:
throw Exception('Invalid Array Helper generated.');}
}
void operator[]=(int index, int value){
_checkBounds(index);
switch(_absoluteIndex+index){
case 0:
_struct._unique_array_item_0 = value;
break;
case 1:
_struct._unique_array_item_1 = value;
break;
default:
throw Exception('Invalid Array Helper generated.');
}
}
}
class ArrayHelperPrefixCollisionTest extends ffi.Struct{
}
abstract class _c_Test {
}
abstract class init_dylib {
}
typedef _c_test1 = ffi.Void Function(
);
typedef _dart_test1 = void Function(
);
typedef _c__test = ffi.Void Function(
);
typedef _dart__test = void Function(
);
typedef _c__c_test = ffi.Void Function(
);
typedef _dart__c_test = void Function(
);
typedef _c__dart_test = ffi.Void Function(
);
typedef _dart__dart_test = void Function(
);
typedef _c_Test1 = ffi.Void Function(
);
typedef _dart_Test = void Function(
);
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
});
test('boolean_dartBool', () {
final library = Library(
name: 'Bindings',
dartBool: true,
bindings: [
Func(
name: 'test1',
returnType: Type.boolean(),
parameters: [
Parameter(name: 'a', type: Type.boolean()),
Parameter(name: 'b', type: Type.pointer(Type.boolean())),
],
),
Struc(
name: 'test2',
members: [
Member(name: 'a', type: Type.boolean()),
],
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/boolean-dartbool-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class Bindings{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
bool test1(
bool a,
ffi.Pointer<ffi.Uint8> b,
) {
return (_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1'))(
a?1:0,
b,
)!=0;
}
_dart_test1? _test1;
}
class test2 extends ffi.Struct{
@ffi.Uint8()
external int a;
}
typedef _c_test1 = ffi.Uint8 Function(
ffi.Uint8 a,
ffi.Pointer<ffi.Uint8> b,
);
typedef _dart_test1 = int Function(
int a,
ffi.Pointer<ffi.Uint8> b,
);
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
test('boolean_no_dartBool', () {
final library = Library(
name: 'Bindings',
dartBool: false,
bindings: [
Func(
name: 'test1',
returnType: Type.boolean(),
parameters: [
Parameter(name: 'a', type: Type.boolean()),
Parameter(name: 'b', type: Type.pointer(Type.boolean())),
],
),
Struc(
name: 'test2',
members: [
Member(name: 'a', type: Type.boolean()),
],
),
],
);
final gen = library.generate();
// Writing to file for debug purpose.
final file = File(
'test/debug_generated/boolean-no-dartBool-output.dart',
);
try {
expect(gen, '''// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
class Bindings{
/// Holds the Dynamic library.
final ffi.DynamicLibrary _dylib;
/// The symbols are looked up in [dynamicLibrary].
Bindings(ffi.DynamicLibrary dynamicLibrary): _dylib = dynamicLibrary;
int test1(
int a,
ffi.Pointer<ffi.Uint8> b,
) {
return (_test1 ??= _dylib.lookupFunction<_c_test1,_dart_test1>('test1'))(
a,
b,
);
}
_dart_test1? _test1;
}
class test2 extends ffi.Struct{
@ffi.Uint8()
external int a;
}
typedef _c_test1 = ffi.Uint8 Function(
ffi.Uint8 a,
ffi.Pointer<ffi.Uint8> b,
);
typedef _dart_test1 = int Function(
int a,
ffi.Pointer<ffi.Uint8> b,
);
''');
if (file.existsSync()) {
file.delete();
}
} catch (e) {
file.writeAsStringSync(gen);
print('Failed test, Debug output: ${file.absolute.path}');
rethrow;
}
});
}