diff options
Diffstat (limited to 'test/fiddle/test_function.rb')
| -rw-r--r-- | test/fiddle/test_function.rb | 172 |
1 files changed, 165 insertions, 7 deletions
diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb index 04c94fb5cb..8ac4f60aa3 100644 --- a/test/fiddle/test_function.rb +++ b/test/fiddle/test_function.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true begin require_relative 'helper' rescue LoadError @@ -8,6 +9,10 @@ module Fiddle def setup super Fiddle.last_error = nil + if WINDOWS + Fiddle.win32_last_error = nil + Fiddle.win32_last_socket_error = nil + end end def test_default_abi @@ -15,20 +20,55 @@ module Fiddle assert_equal Function::DEFAULT, func.abi end + def test_name + func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE, name: 'sin') + assert_equal 'sin', func.name + end + + def test_need_gvl? + libruby = Fiddle.dlopen(nil) + rb_str_dup = Function.new(libruby['rb_str_dup'], + [:voidp], + :voidp, + need_gvl: true) + assert(rb_str_dup.need_gvl?) + assert_equal('Hello', + Fiddle.dlunwrap(rb_str_dup.call(Fiddle.dlwrap('Hello')))) + end + def test_argument_errors - assert_raises(TypeError) do + assert_raise(TypeError) do Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE) end - assert_raises(TypeError) do + assert_raise(TypeError) do Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE) end - assert_raises(TypeError) do + assert_raise(TypeError) do Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo') end end + def test_argument_type_conversion + type = Struct.new(:int, :call_count) do + def initialize(int) + super(int, 0) + end + def to_int + raise "exhausted" if (self.call_count += 1) > 1 + self.int + end + end + type_arg = type.new(TYPE_DOUBLE) + type_result = type.new(TYPE_DOUBLE) + assert_nothing_raised(RuntimeError) do + Function.new(@libm['sin'], [type_arg], type_result) + end + assert_equal(1, type_arg.call_count) + assert_equal(1, type_result.call_count) + end + def test_call func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE) assert_in_delta 1.0, func.call(90 * Math::PI / 180), 0.0001 @@ -42,10 +82,10 @@ module Fiddle }.new(TYPE_INT, [TYPE_INT]) func = Function.new(closure, [TYPE_INT], TYPE_INT) - assert_raises(ArgumentError) do + assert_raise(ArgumentError) do func.call(1,2,3) end - assert_raises(ArgumentError) do + assert_raise(ArgumentError) do func.call end end @@ -54,16 +94,134 @@ module Fiddle func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) assert_nil Fiddle.last_error - str = func.call("000", "123") + func.call(+"000", "123") refute_nil Fiddle.last_error end + if WINDOWS + def test_win32_last_error + kernel32 = Fiddle.dlopen("kernel32") + args = [kernel32["SetLastError"], [-TYPE_LONG], TYPE_VOID] + args << Function::STDCALL if Function.const_defined?(:STDCALL) + set_last_error = Function.new(*args) + assert_nil(Fiddle.win32_last_error) + n = 1 << 29 | 1 + set_last_error.call(n) + assert_equal(n, Fiddle.win32_last_error) + end + + def test_win32_last_socket_error + ws2_32 = Fiddle.dlopen("ws2_32") + args = [ws2_32["WSASetLastError"], [TYPE_INT], TYPE_VOID] + args << Function::STDCALL if Function.const_defined?(:STDCALL) + wsa_set_last_error = Function.new(*args) + assert_nil(Fiddle.win32_last_socket_error) + n = 1 << 29 | 1 + wsa_set_last_error.call(n) + assert_equal(n, Fiddle.win32_last_socket_error) + end + end + def test_strcpy f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) - buff = "000" + buff = +"000" str = f.call(buff, "123") assert_equal("123", buff) assert_equal("123", str.to_s) end + + def call_proc(string_to_copy) + buff = +"000" + str = yield(buff, string_to_copy) + [buff, str] + end + + def test_function_as_proc + f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) + buff, str = call_proc("123", &f) + assert_equal("123", buff) + assert_equal("123", str.to_s) + end + + def test_function_as_method + f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) + klass = Class.new do + define_singleton_method(:strcpy, &f) + end + buff = +"000" + str = klass.strcpy(buff, "123") + assert_equal("123", buff) + assert_equal("123", str.to_s) + end + + def test_nogvl_poll + # XXX hack to quiet down CI errors on EINTR from r64353 + # [ruby-core:88360] [Misc #14937] + # Making pipes (and sockets) non-blocking by default would allow + # us to get rid of POSIX timers / timer pthread + # https://bugs.ruby-lang.org/issues/14968 + IO.pipe { |r,w| IO.select([r], [w]) } + begin + poll = @libc['poll'] + rescue Fiddle::DLError + omit 'poll(2) not available' + end + f = Function.new(poll, [TYPE_VOIDP, TYPE_INT, TYPE_INT], TYPE_INT) + + msec = 200 + t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) + th = Thread.new { f.call(nil, 0, msec) } + n1 = f.call(nil, 0, msec) + n2 = th.value + t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) + assert_in_delta(msec, t1 - t0, 180, 'slept amount of time') + assert_equal(0, n1, perror("poll(2) in main-thread")) + assert_equal(0, n2, perror("poll(2) in sub-thread")) + end + + def test_no_memory_leak + if respond_to?(:assert_nothing_leaked_memory) + rb_obj_frozen_p_symbol = Fiddle.dlopen(nil)["rb_obj_frozen_p"] + rb_obj_frozen_p = Fiddle::Function.new(rb_obj_frozen_p_symbol, + [Fiddle::TYPE_UINTPTR_T], + Fiddle::TYPE_UINTPTR_T) + a = "a" + n_tries = 100_000 + n_tries.times do + begin + a + 1 + rescue TypeError + end + end + n_arguments = 1 + sizeof_fiddle_generic = Fiddle::SIZEOF_VOIDP # Rough + size_per_try = + (sizeof_fiddle_generic * n_arguments) + + (Fiddle::SIZEOF_VOIDP * (n_arguments + 1)) + assert_nothing_leaked_memory(size_per_try * n_tries) do + n_tries.times do + begin + rb_obj_frozen_p.call(a) + rescue TypeError + end + end + end + else + prep = 'r = Fiddle::Function.new(Fiddle.dlopen(nil)["rb_obj_frozen_p"], [Fiddle::TYPE_UINTPTR_T], Fiddle::TYPE_UINTPTR_T); a = "a"' + code = 'begin r.call(a); rescue TypeError; end' + assert_no_memory_leak(%w[-W0 -rfiddle], "#{prep}\n1000.times{#{code}}", "10_000.times {#{code}}", limit: 1.2) + end + end + + private + + def perror(m) + proc do + if e = Fiddle.last_error + m = "#{m}: #{SystemCallError.new(e).message}" + end + m + end + end end end if defined?(Fiddle) |
