ffi
Version:
A foreign function interface (FFI) for Node.js
220 lines (190 loc) • 7.42 kB
JavaScript
var assert = require('assert')
, ref = require('ref')
, Array = require('ref-array')
, Struct = require('ref-struct')
, ffi = require('../')
, bindings = require('bindings')({ module_root: __dirname, bindings: 'ffi_tests' })
describe('ForeignFunction', function () {
afterEach(gc)
// these structs are also defined in ffi_tests.cc
var box = Struct({
width: ref.types.int
, height: ref.types.int
})
var arst = Struct({
num: 'int'
, array: Array('double', 20)
})
it('should call the static "abs" bindings', function () {
var _abs = bindings.abs
var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
assert.equal('function', typeof abs)
assert.equal(1234, abs(-1234))
})
it('should throw an Error with a meaningful message when type\'s `set()` throws', function () {
var _abs = bindings.abs
var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
assert.throws(function () {
// Changed, because returning string is not failing because of this; https://github.com/iojs/io.js/issues/1161
abs(11111111111111111111)
}, /error setting argument 0/)
})
it('should call the static "atoi" bindings', function () {
var _atoi = bindings.atoi
var atoi = ffi.ForeignFunction(_atoi, 'int', [ 'string' ])
assert.equal('function', typeof atoi)
assert.equal(1234, atoi('1234'))
})
it('should call the static "double_box" bindings', function () {
var double_box = ffi.ForeignFunction(bindings.double_box, box, [ box ])
var b = new box
assert(b instanceof box)
b.width = 4
b.height = 5
var out = double_box(b)
// double_box writes to its input "box" struct, so make sure that the one we
// passed in remains unaffected (since we passed it in by value, not pointer)
assert.equal(4, b.width)
assert.equal(5, b.height)
assert(out instanceof box)
assert.equal(8, out.width)
assert.equal(10, out.height)
assert.notEqual(b.ref().address(), out.ref().address())
})
it('should call the static "double_box_ptr" bindings', function () {
var boxPtr = ref.refType(box)
var double_box_ptr = ffi.ForeignFunction(bindings.double_box_ptr, box, [ boxPtr ])
var b = new box
b.width = 4
b.height = 5
var out = double_box_ptr(b.ref())
// double_box_ptr writes to its input "box" struct, so make sure that the one
// we passed in has it's values changed (since we passed it in by pointer)
assert.equal(8, b.width)
assert.equal(10, b.height)
assert(out instanceof box)
assert.equal(8, out.width)
assert.equal(10, out.height)
assert.notEqual(b.ref().address(), out.ref().address())
})
it('should call the static "area_box" bindings', function () {
var area_box = ffi.ForeignFunction(bindings.area_box, ref.types.int, [ box ])
var b = new box({ width: 5, height: 20 })
var rtn = area_box(b)
assert.equal('number', typeof rtn)
assert.equal(100, rtn)
})
it('should call the static "area_box_ptr" bindings', function () {
var boxPtr = ref.refType(box)
var area_box = ffi.ForeignFunction(bindings.area_box_ptr, ref.types.int, [ boxPtr ])
var b = new box({ width: 5, height: 20 })
var rtn = area_box(b.ref())
assert.equal('number', typeof rtn)
assert.equal(100, rtn)
})
it('should call the static "create_box" bindings', function () {
var create_box = ffi.ForeignFunction(bindings.create_box, box, [ 'int', 'int' ])
var rtn = create_box(1, 2)
assert(rtn instanceof box)
assert.equal(1, rtn.width)
assert.equal(2, rtn.height)
})
it('should call the static "add_boxes" bindings', function () {
var count = 3
var boxes = new Buffer(box.size * count)
box.set(boxes, box.size * 0, { width: 1, height: 10 })
box.set(boxes, box.size * 1, { width: 2, height: 20 })
box.set(boxes, box.size * 2, { width: 3, height: 30 })
var boxPtr = ref.refType(box)
var add_boxes = ffi.ForeignFunction(bindings.add_boxes, box, [ boxPtr, 'int' ])
var rtn = add_boxes(boxes, count)
assert(rtn instanceof box)
assert.equal(6, rtn.width)
assert.equal(60, rtn.height)
})
it('should call the static "int_array" bindings', function () {
var IntArray = Array('int')
var int_array = ffi.ForeignFunction(bindings.int_array, IntArray, [ IntArray ])
var array = new IntArray([ 1, 2, 3, 4, 5, -1 ])
var out = int_array(array)
out.length = array.length
assert.equal(2, out[0])
assert.equal(4, out[1])
assert.equal(6, out[2])
assert.equal(8, out[3])
assert.equal(10, out[4])
assert.equal(-1, out[5])
})
it('should call the static "array_in_struct" bindings', function () {
var array_in_struct = ffi.ForeignFunction(bindings.array_in_struct, arst, [ arst ])
var a = new arst
assert.equal(20, a.array.length)
a.num = 69
for (var i = 0; i < 20; i++) {
a.array[i] = i / 3.14
}
var b = array_in_struct(a)
assert(b instanceof arst)
assert.equal(138, b.num)
assert.equal(20, b.array.length)
for (var i = 0; i < 20; i++) {
// Math.round() because of floating point rounding erros
assert.equal(i, Math.round(b.array[i]))
}
})
// allow a Buffer backing store to be used as a "string" FFI argument
// https://github.com/node-ffi/node-ffi/issues/169
it('should call the static "test_169" bindings', function () {
var test = ffi.ForeignFunction(bindings.test_169, 'int', [ 'string', 'int' ])
var b = new Buffer(20)
var len = test(b, b.length)
assert.equal('sample str', b.toString('ascii', 0, len))
})
// testing `bool` ref type
// https://github.com/TooTallNate/ref/issues/56
it('should call the static "test_169" bindings', function () {
var Obj56 = Struct({
'traceMode': ref.types.bool
})
var t = new Obj56({ traceMode: true })
var f = new Obj56({ traceMode: false })
var test = ffi.ForeignFunction(bindings.test_ref_56, 'int', [ ref.refType(Obj56) ])
assert.equal(1, test(t.ref()))
assert.equal(0, test(f.ref()))
})
it('should not call the "ref()" function of its arguments', function () {
var void_ptr_arg = ffi.ForeignFunction(bindings.abs, 'void *', [ 'void *' ])
var b = new Buffer(0)
b.ref = assert.bind(null, 0, '"ref()" should not be called')
void_ptr_arg(b)
})
describe('async', function () {
it('should call the static "abs" bindings asynchronously', function (done) {
var _abs = bindings.abs
var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
assert.equal('function', typeof abs.async)
// invoke asynchronously
abs.async(-1234, function (err, res) {
assert.equal(null, err)
assert.equal(1234, res)
done()
})
})
it('should invoke the callback with an Error with a meaningful message when type\'s `set()` throws', function (done) {
var _abs = bindings.abs
var abs = ffi.ForeignFunction(_abs, 'int', [ 'int' ])
// Changed, because returning string is not failing because of this; https://github.com/iojs/io.js/issues/1161
abs.async(1111111111111111111111, function (err, res) {
try {
assert(err)
assert(/error setting argument 0/.test(err.message))
assert.equal('undefined', typeof res)
done()
}
catch (e) {
done(e)
}
})
})
})
})