From c1fb6a82dcfbd308390d380d62c7127635fc66db Mon Sep 17 00:00:00 2001 From: tenderlove Date: Tue, 27 Nov 2012 19:54:50 +0000 Subject: * ext/fiddle/handle.c: Make Fiddle independent of DL, copy DL::Handle to Fiddle::Handle. * ext/fiddle/pointer.c: Make Fiddle independent of DL, copy DL::Pointer to Fiddle::Pointer. * test/fiddle/test_func.rb: relevent tests * test/fiddle/test_handle.rb: ditto * test/fiddle/test_pointer.rb: ditto * ext/dl/lib/dl/struct.rb: use Fiddle::Pointer if available * ext/fiddle/extconf.rb: check for dlfcn.h * ext/fiddle/fiddle.c: add constants for sizeof() things * ext/fiddle/fiddle.h: include dlfcn.h * ext/fiddle/function.c: expose a C function for creating new Fiddle::Function objects. * ext/fiddle/lib/fiddle.rb: include constants for dl backwards compat * ext/fiddle/lib/fiddle/function.rb: read the pointer from the function for dl backwards compat. * test/dl/test_callback.rb: check the addresses of the pointers rather than their types. * test/fiddle/helper.rb: remove dependency on dl * test/fiddle/test_closure.rb: ditto * test/fiddle/test_fiddle.rb: ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37907 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- test/fiddle/helper.rb | 5 +- test/fiddle/test_closure.rb | 4 +- test/fiddle/test_fiddle.rb | 21 ---- test/fiddle/test_func.rb | 79 +++++++++++++++ test/fiddle/test_handle.rb | 186 +++++++++++++++++++++++++++++++++++ test/fiddle/test_pointer.rb | 231 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 500 insertions(+), 26 deletions(-) create mode 100644 test/fiddle/test_func.rb create mode 100644 test/fiddle/test_handle.rb create mode 100644 test/fiddle/test_pointer.rb (limited to 'test/fiddle') diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb index ad69f55e69..209dedb6fc 100644 --- a/test/fiddle/helper.rb +++ b/test/fiddle/helper.rb @@ -1,5 +1,4 @@ require 'minitest/autorun' -require 'dl' require 'fiddle' # FIXME: this is stolen from DL and needs to be refactored. @@ -104,8 +103,8 @@ Fiddle::LIBM_SO = libm_so module Fiddle class TestCase < MiniTest::Unit::TestCase def setup - @libc = DL.dlopen(LIBC_SO) - @libm = DL.dlopen(LIBM_SO) + @libc = Fiddle.dlopen(LIBC_SO) + @libm = Fiddle.dlopen(LIBM_SO) end end end diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb index 381b3f96c1..56839e7b63 100644 --- a/test/fiddle/test_closure.rb +++ b/test/fiddle/test_closure.rb @@ -57,8 +57,8 @@ module Fiddle end %w[INT SHORT CHAR LONG LONG_LONG].each do |name| - type = DL.const_get("TYPE_#{name}") rescue next - size = DL.const_get("SIZEOF_#{name}") + type = Fiddle.const_get("TYPE_#{name}") rescue next + size = Fiddle.const_get("SIZEOF_#{name}") [[type, size-1, name], [-type, size, "unsigned_"+name]].each do |t, s, n| define_method("test_conversion_#{n.downcase}") do arg = nil diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb index f910978af5..4c6ab97b12 100644 --- a/test/fiddle/test_fiddle.rb +++ b/test/fiddle/test_fiddle.rb @@ -4,27 +4,6 @@ rescue LoadError end class TestFiddle < Fiddle::TestCase - def test_constants_match - [ - :TYPE_VOID, - :TYPE_VOIDP, - :TYPE_CHAR, - :TYPE_SHORT, - :TYPE_INT, - :TYPE_LONG, - :TYPE_LONG_LONG, - :TYPE_FLOAT, - :TYPE_DOUBLE, - :TYPE_SIZE_T, - :TYPE_SSIZE_T, - :TYPE_PTRDIFF_T, - :TYPE_INTPTR_T, - :TYPE_UINTPTR_T, - ].each do |name| - assert_equal(DL.const_get(name), Fiddle.const_get(name), "Fiddle::#{name}") - end - end - def test_windows_constant require 'rbconfig' if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb new file mode 100644 index 0000000000..e77229b7e8 --- /dev/null +++ b/test/fiddle/test_func.rb @@ -0,0 +1,79 @@ +require_relative 'helper' + +module Fiddle + class TestFunc < TestCase + def test_random + f = Function.new(@libc['srand'], [-TYPE_LONG], TYPE_VOID) + assert_nil f.call(10) + end + + def test_sinf + begin + f = Function.new(@libm['sinf'], [TYPE_FLOAT], TYPE_FLOAT) + rescue Fiddle::DLError + skip "libm may not have sinf()" + end + assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001 + end + + def test_sin + f = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE) + assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001 + end + + def test_string + stress, GC.stress = GC.stress, true + f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) + buff = "000" + str = f.call(buff, "123") + assert_equal("123", buff) + assert_equal("123", str.to_s) + ensure + GC.stress = stress + end + + def test_isdigit + f = Function.new(@libc['isdigit'], [TYPE_INT], TYPE_INT) + r1 = f.call(?1.ord) + r2 = f.call(?2.ord) + rr = f.call(?r.ord) + assert_operator r1, :>, 0 + assert_operator r2, :>, 0 + assert_equal 0, rr + end + + def test_atof + f = Function.new(@libc['atof'], [TYPE_VOIDP], TYPE_DOUBLE) + r = f.call("12.34") + assert_includes(12.00..13.00, r) + end + + def test_strtod + f = Function.new(@libc['strtod'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_DOUBLE) + buff1 = Pointer["12.34"] + buff2 = buff1 + 4 + r = f.call(buff1, - buff2) + assert_in_delta(12.34, r, 0.001) + end + + def test_qsort1 + cb = Class.new(Closure) { + def call(x, y) + Pointer.new(x)[0] <=> Pointer.new(y)[0] + end + }.new(TYPE_INT, [TYPE_VOIDP, TYPE_VOIDP]) + + qsort = Function.new(@libc['qsort'], + [TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP], + TYPE_VOID) + buff = "9341" + qsort.call(buff, buff.size, 1, cb) + assert_equal("1349", buff) + + bug4929 = '[ruby-core:37395]' + buff = "9341" + EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)} + assert_equal("1349", buff, bug4929) + end + end +end diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb new file mode 100644 index 0000000000..f11272abbf --- /dev/null +++ b/test/fiddle/test_handle.rb @@ -0,0 +1,186 @@ +require_relative 'helper' + +module Fiddle + class TestHandle < TestCase + include Fiddle + + def test_to_i + handle = Fiddle::Handle.new(LIBC_SO) + assert_kind_of Integer, handle.to_i + end + + def test_static_sym_secure + assert_raises(SecurityError) do + Thread.new do + $SAFE = 2 + Fiddle::Handle.sym('calloc') + end.join + end + end + + def test_static_sym_unknown + assert_raises(DLError) { Fiddle::Handle.sym('fooo') } + assert_raises(DLError) { Fiddle::Handle['fooo'] } + end + + def test_static_sym + skip "Fiddle::Handle.sym is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM + begin + # Linux / Darwin / FreeBSD + refute_nil Fiddle::Handle.sym('dlopen') + assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen'] + rescue + # NetBSD + require 'objspace' + refute_nil Fiddle::Handle.sym('Init_objspace') + assert_equal Fiddle::Handle.sym('Init_objspace'), Fiddle::Handle['Init_objspace'] + end + end + + def test_sym_closed_handle + handle = Fiddle::Handle.new(LIBC_SO) + handle.close + assert_raises(DLError) { handle.sym("calloc") } + assert_raises(DLError) { handle["calloc"] } + end + + def test_sym_unknown + handle = Fiddle::Handle.new(LIBC_SO) + assert_raises(DLError) { handle.sym('fooo') } + assert_raises(DLError) { handle['fooo'] } + end + + def test_sym_with_bad_args + handle = Handle.new(LIBC_SO) + assert_raises(TypeError) { handle.sym(nil) } + assert_raises(TypeError) { handle[nil] } + end + + def test_sym_secure + assert_raises(SecurityError) do + Thread.new do + $SAFE = 2 + handle = Handle.new(LIBC_SO) + handle.sym('calloc') + end.join + end + end + + def test_sym + handle = Handle.new(LIBC_SO) + refute_nil handle.sym('calloc') + refute_nil handle['calloc'] + end + + def test_handle_close + handle = Handle.new(LIBC_SO) + assert_equal 0, handle.close + end + + def test_handle_close_twice + handle = Handle.new(LIBC_SO) + handle.close + assert_raises(DLError) do + handle.close + end + end + + def test_dlopen_returns_handle + assert_instance_of Handle, dlopen(LIBC_SO) + end + + def test_dlopen_safe + assert_raises(SecurityError) do + Thread.new do + $SAFE = 2 + dlopen(LIBC_SO) + end.join + end + end + + def test_initialize_safe + assert_raises(SecurityError) do + Thread.new do + $SAFE = 2 + Handle.new(LIBC_SO) + end.join + end + end + + def test_initialize_noargs + handle = Handle.new + refute_nil handle['rb_str_new'] + end + + def test_initialize_flags + handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL) + refute_nil handle['calloc'] + end + + def test_enable_close + handle = Handle.new(LIBC_SO) + assert !handle.close_enabled?, 'close is enabled' + + handle.enable_close + assert handle.close_enabled?, 'close is not enabled' + end + + def test_disable_close + handle = Handle.new(LIBC_SO) + + handle.enable_close + assert handle.close_enabled?, 'close is enabled' + handle.disable_close + assert !handle.close_enabled?, 'close is enabled' + end + + def test_NEXT + begin + # Linux / Darwin + # + # There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find + # the first occurrence of the desired symbol using the default library search order. The + # latter will find the next occurrence of a function in the search order after the current + # library. This allows one to provide a wrapper around a function in another shared + # library. + # --- Ubuntu Linux 8.04 dlsym(3) + handle = Handle::NEXT + refute_nil handle['malloc'] + rescue + # BSD + # + # If dlsym() is called with the special handle RTLD_NEXT, then the search + # for the symbol is limited to the shared objects which were loaded after + # the one issuing the call to dlsym(). Thus, if the function is called + # from the main program, all the shared libraries are searched. If it is + # called from a shared library, all subsequent shared libraries are + # searched. RTLD_NEXT is useful for implementing wrappers around library + # functions. For example, a wrapper function getpid() could access the + # "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc() + # interface, below, should be used, since getpid() is a function and not a + # data object.) + # --- FreeBSD 8.0 dlsym(3) + require 'objspace' + handle = Handle::NEXT + refute_nil handle['Init_objspace'] + end + end unless /mswin|mingw/ =~ RUBY_PLATFORM + + def test_DEFAULT + skip "Handle::DEFAULT is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM + handle = Handle::DEFAULT + refute_nil handle['malloc'] + end unless /mswin|mingw/ =~ RUBY_PLATFORM + + def test_dlerror + # FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls + # getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if + # it calls _nss_cache_cycle_prevention_function with dlsym(3). + # So our Fiddle::Handle#sym must call dlerror(3) before call dlsym. + # In general uses of dlerror(3) should call it before use it. + require 'socket' + Socket.gethostbyname("localhost") + Fiddle.dlopen("/usr/lib/libc.so").sym('strcpy') + end if /freebsd/=~ RUBY_PLATFORM + end +end diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb new file mode 100644 index 0000000000..ef33b91062 --- /dev/null +++ b/test/fiddle/test_pointer.rb @@ -0,0 +1,231 @@ +require_relative 'helper' +require_relative '../ruby/envutil' + +module Fiddle + class TestPointer < TestCase + def dlwrap arg + Fiddle.dlwrap arg + end + + include Test::Unit::Assertions + + def test_cptr_to_int + null = Fiddle::NULL + assert_equal(null.to_i, null.to_int) + end + + def test_malloc_free_func_int + free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID) + assert_equal free.to_i, Fiddle::RUBY_FREE.to_i + + ptr = Pointer.malloc(10, free.to_i) + assert_equal 10, ptr.size + assert_equal free.to_i, ptr.free.to_i + end + + def test_malloc_free_func + free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID) + + ptr = Pointer.malloc(10, free) + assert_equal 10, ptr.size + assert_equal free.to_i, ptr.free.to_i + end + + def test_to_str + str = "hello world" + ptr = Pointer[str] + + assert_equal 3, ptr.to_str(3).length + assert_equal str, ptr.to_str + + ptr[5] = 0 + assert_equal "hello\0world", ptr.to_str + end + + def test_to_s + str = "hello world" + ptr = Pointer[str] + + assert_equal 3, ptr.to_s(3).length + assert_equal str, ptr.to_s + + ptr[5] = 0 + assert_equal 'hello', ptr.to_s + end + + def test_minus + str = "hello world" + ptr = Pointer[str] + assert_equal ptr.to_s, (ptr + 3 - 3).to_s + end + + # TODO: what if the pointer size is 0? raise an exception? do we care? + def test_plus + str = "hello world" + ptr = Pointer[str] + new_str = ptr + 3 + assert_equal 'lo world', new_str.to_s + end + + def test_inspect + ptr = Pointer.new(0) + inspect = ptr.inspect + assert_match(/size=#{ptr.size}/, inspect) + assert_match(/free=#{sprintf("%#x", ptr.free.to_i)}/, inspect) + assert_match(/ptr=#{sprintf("%#x", ptr.to_i)}/, inspect) + end + + def test_to_ptr_string + str = "hello world" + ptr = Pointer[str] + assert ptr.tainted?, 'pointer should be tainted' + assert_equal str.length, ptr.size + assert_equal 'hello', ptr[0,5] + end + + def test_to_ptr_io + buf = Pointer.malloc(10) + File.open(__FILE__, 'r') do |f| + ptr = Pointer.to_ptr f + fread = Function.new(@libc['fread'], + [TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP], + TYPE_INT) + fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i) + end + + File.open(__FILE__, 'r') do |f| + assert_equal f.read(9), buf.to_s + end + end + + def test_to_ptr_with_ptr + ptr = Pointer.new 0 + ptr2 = Pointer.to_ptr Struct.new(:to_ptr).new(ptr) + assert_equal ptr, ptr2 + + assert_raises(Fiddle::DLError) do + Pointer.to_ptr Struct.new(:to_ptr).new(nil) + end + end + + def test_to_ptr_with_num + ptr = Pointer.new 0 + assert_equal ptr, Pointer[0] + end + + def test_equals + ptr = Pointer.new 0 + ptr2 = Pointer.new 0 + assert_equal ptr2, ptr + end + + def test_not_equals + ptr = Pointer.new 0 + refute_equal 10, ptr, '10 should not equal the pointer' + end + + def test_cmp + ptr = Pointer.new 0 + assert_nil(ptr <=> 10, '10 should not be comparable') + end + + def test_ref_ptr + ary = [0,1,2,4,5] + addr = Pointer.new(dlwrap(ary)) + assert_equal addr.to_i, addr.ref.ptr.to_i + + assert_equal addr.to_i, (+ (- addr)).to_i + end + + def test_to_value + ary = [0,1,2,4,5] + addr = Pointer.new(dlwrap(ary)) + assert_equal ary, addr.to_value + end + + def test_free + ptr = Pointer.malloc(4) + assert_nil ptr.free + end + + def test_free= + assert_normal_exit(<<-"End", '[ruby-dev:39269]') + require 'fiddle' + Fiddle::LIBC_SO = #{Fiddle::LIBC_SO.dump} + Fiddle::LIBM_SO = #{Fiddle::LIBM_SO.dump} + include Fiddle + @libc = dlopen(LIBC_SO) + @libm = dlopen(LIBM_SO) + free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID) + ptr = Fiddle::Pointer.malloc(4) + ptr.free = free + free.ptr + ptr.free.ptr + End + + free = Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID) + ptr = Pointer.malloc(4) + ptr.free = free + + assert_equal free.ptr, ptr.free.ptr + end + + def test_null? + ptr = Pointer.new(0) + assert ptr.null? + end + + def test_size + ptr = Pointer.malloc(4) + assert_equal 4, ptr.size + Fiddle.free ptr.to_i + end + + def test_size= + ptr = Pointer.malloc(4) + ptr.size = 10 + assert_equal 10, ptr.size + Fiddle.free ptr.to_i + end + + def test_aref_aset + check = Proc.new{|str,ptr| + assert_equal(str.size(), ptr.size()) + assert_equal(str, ptr.to_s()) + assert_equal(str[0,2], ptr.to_s(2)) + assert_equal(str[0,2], ptr[0,2]) + assert_equal(str[1,2], ptr[1,2]) + assert_equal(str[1,0], ptr[1,0]) + assert_equal(str[0].ord, ptr[0]) + assert_equal(str[1].ord, ptr[1]) + } + str = 'abc' + ptr = Pointer[str] + check.call(str, ptr) + + str[0] = "c" + assert_equal 'c'.ord, ptr[0] = "c".ord + check.call(str, ptr) + + str[0,2] = "aa" + assert_equal 'aa', ptr[0,2] = "aa" + check.call(str, ptr) + + ptr2 = Pointer['cdeeee'] + str[0,2] = "cd" + assert_equal ptr2, ptr[0,2] = ptr2 + check.call(str, ptr) + + ptr3 = Pointer['vvvv'] + str[0,2] = "vv" + assert_equal ptr3.to_i, ptr[0,2] = ptr3.to_i + check.call(str, ptr) + end + + def test_null_pointer + nullpo = Pointer.new(0) + assert_raise(DLError) {nullpo[0]} + assert_raise(DLError) {nullpo[0] = 1} + end + end +end -- cgit v1.2.3