blob: dd4d2eaaf6da212b24d2788a1068823290d2cace [file] [log] [blame]
#
# This file is part of ruby-ffi.
# For licensing, see LICENSE.SPECS
#
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
describe FFI::Function do
module LibTest
extend FFI::Library
ffi_lib TestLibrary::PATH
attach_function :testFunctionAdd, [:int, :int, :pointer], :int
end
before do
@libtest = FFI::DynamicLibrary.open(TestLibrary::PATH,
FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL)
end
it 'is initialized with a signature and a block' do
fn = FFI::Function.new(:int, []) { 5 }
expect(fn.call).to eql 5
end
it 'raises an error when passing a wrong signature' do
expect { FFI::Function.new([], :int).new { } }.to raise_error TypeError
end
it 'returns a native pointer' do
expect(FFI::Function.new(:int, []) { }).to be_a_kind_of FFI::Pointer
end
it 'can be used as callback from C passing to it a block' do
function_add = FFI::Function.new(:int, [:int, :int]) { |a, b| a + b }
expect(LibTest.testFunctionAdd(10, 10, function_add)).to eq(20)
end
it 'can be used as callback from C passing to it a Proc object' do
function_add = FFI::Function.new(:int, [:int, :int], Proc.new { |a, b| a + b })
expect(LibTest.testFunctionAdd(10, 10, function_add)).to eq(20)
end
it 'can be used to wrap an existing function pointer' do
expect(FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd')).call(10, 10)).to eq(20)
end
it 'can be attached to a module' do
module Foo; end
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
fp.attach(Foo, 'add')
expect(Foo.add(10, 10)).to eq(20)
end
it 'can be used to extend an object' do
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
foo = Object.new
class << foo
def singleton_class
class << self; self; end
end
end
fp.attach(foo.singleton_class, 'add')
expect(foo.add(10, 10)).to eq(20)
end
it 'can wrap a blocking function' do
fpOpen = FFI::Function.new(:pointer, [ ], @libtest.find_function('testBlockingOpen'))
fpRW = FFI::Function.new(:char, [ :pointer, :char ], @libtest.find_function('testBlockingRW'), :blocking => true)
fpWR = FFI::Function.new(:char, [ :pointer, :char ], @libtest.find_function('testBlockingWR'), :blocking => true)
fpClose = FFI::Function.new(:void, [ :pointer ], @libtest.find_function('testBlockingClose'))
handle = fpOpen.call
expect(handle).not_to be_null
begin
thWR = Thread.new { fpWR.call(handle, 63) }
thRW = Thread.new { fpRW.call(handle, 64) }
expect(thWR.value).to eq(64)
expect(thRW.value).to eq(63)
ensure
fpClose.call(handle)
end
end
it 'autorelease flag is set to true by default' do
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
expect(fp.autorelease?).to be true
end
it 'can explicity free itself' do
fp = FFI::Function.new(:int, []) { }
fp.free
expect { fp.free }.to raise_error RuntimeError
end
it 'can\'t explicity free itself if not previously allocated' do
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
expect { fp.free }.to raise_error RuntimeError
end
end