summaryrefslogtreecommitdiff
path: root/test/fiddle
diff options
context:
space:
mode:
Diffstat (limited to 'test/fiddle')
-rw-r--r--test/fiddle/helper.rb123
-rw-r--r--test/fiddle/test_c_struct_entry.rb77
-rw-r--r--test/fiddle/test_c_union_entity.rb35
-rw-r--r--test/fiddle/test_closure.rb85
-rw-r--r--test/fiddle/test_cparser.rb211
-rw-r--r--test/fiddle/test_fiddle.rb17
-rw-r--r--test/fiddle/test_func.rb93
-rw-r--r--test/fiddle/test_function.rb100
-rw-r--r--test/fiddle/test_handle.rb194
-rw-r--r--test/fiddle/test_import.rb151
-rw-r--r--test/fiddle/test_pointer.rb236
11 files changed, 1322 insertions, 0 deletions
diff --git a/test/fiddle/helper.rb b/test/fiddle/helper.rb
new file mode 100644
index 0000000000..4aaa55ea78
--- /dev/null
+++ b/test/fiddle/helper.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'fiddle'
+
+# FIXME: this is stolen from DL and needs to be refactored.
+
+libc_so = libm_so = nil
+
+case RUBY_PLATFORM
+when /cygwin/
+ libc_so = "cygwin1.dll"
+ libm_so = "cygwin1.dll"
+when /linux/
+ libdir = '/lib'
+ case [0].pack('L!').size
+ when 4
+ # 32-bit ruby
+ libdir = '/lib32' if File.directory? '/lib32'
+ when 8
+ # 64-bit ruby
+ libdir = '/lib64' if File.directory? '/lib64'
+ end
+ libc_so = File.join(libdir, "libc.so.6")
+ libm_so = File.join(libdir, "libm.so.6")
+when /mingw/, /mswin/
+ require "rbconfig"
+ crtname = RbConfig::CONFIG["RUBY_SO_NAME"][/msvc\w+/] || 'ucrtbase'
+ libc_so = libm_so = "#{crtname}.dll"
+when /darwin/
+ libc_so = "/usr/lib/libc.dylib"
+ libm_so = "/usr/lib/libm.dylib"
+when /kfreebsd/
+ libc_so = "/lib/libc.so.0.1"
+ libm_so = "/lib/libm.so.1"
+when /gnu/ #GNU/Hurd
+ libc_so = "/lib/libc.so.0.3"
+ libm_so = "/lib/libm.so.6"
+when /mirbsd/
+ libc_so = "/usr/lib/libc.so.41.10"
+ libm_so = "/usr/lib/libm.so.7.0"
+when /freebsd/
+ libc_so = "/lib/libc.so.7"
+ libm_so = "/lib/libm.so.5"
+when /bsd|dragonfly/
+ libc_so = "/usr/lib/libc.so"
+ libm_so = "/usr/lib/libm.so"
+when /solaris/
+ libdir = '/lib'
+ case [0].pack('L!').size
+ when 4
+ # 32-bit ruby
+ libdir = '/lib' if File.directory? '/lib'
+ when 8
+ # 64-bit ruby
+ libdir = '/lib/64' if File.directory? '/lib/64'
+ end
+ libc_so = File.join(libdir, "libc.so")
+ libm_so = File.join(libdir, "libm.so")
+when /aix/
+ pwd=Dir.pwd
+ libc_so = libm_so = "#{pwd}/libaixdltest.so"
+ unless File.exist? libc_so
+ cobjs=%w!strcpy.o!
+ mobjs=%w!floats.o sin.o!
+ funcs=%w!sin sinf strcpy strncpy!
+ expfile='dltest.exp'
+ require 'tmpdir'
+ Dir.mktmpdir do |dir|
+ begin
+ Dir.chdir dir
+ %x!/usr/bin/ar x /usr/lib/libc.a #{cobjs.join(' ')}!
+ %x!/usr/bin/ar x /usr/lib/libm.a #{mobjs.join(' ')}!
+ %x!echo "#{funcs.join("\n")}\n" > #{expfile}!
+ require 'rbconfig'
+ if RbConfig::CONFIG["GCC"] = 'yes'
+ lflag='-Wl,'
+ else
+ lflag=''
+ end
+ flags="#{lflag}-bE:#{expfile} #{lflag}-bnoentry -lm"
+ %x!#{RbConfig::CONFIG["LDSHARED"]} -o #{libc_so} #{(cobjs+mobjs).join(' ')} #{flags}!
+ ensure
+ Dir.chdir pwd
+ end
+ end
+ end
+else
+ libc_so = ARGV[0] if ARGV[0] && ARGV[0][0] == ?/
+ libm_so = ARGV[1] if ARGV[1] && ARGV[1][0] == ?/
+ if( !(libc_so && libm_so) )
+ $stderr.puts("libc and libm not found: #{$0} <libc> <libm>")
+ end
+end
+
+libc_so = nil if !libc_so || (libc_so[0] == ?/ && !File.file?(libc_so))
+libm_so = nil if !libm_so || (libm_so[0] == ?/ && !File.file?(libm_so))
+
+if !libc_so || !libm_so
+ ruby = EnvUtil.rubybin
+ ldd = `ldd #{ruby}`
+ #puts ldd
+ libc_so = $& if !libc_so && %r{/\S*/libc\.so\S*} =~ ldd
+ libm_so = $& if !libm_so && %r{/\S*/libm\.so\S*} =~ ldd
+ #p [libc_so, libm_so]
+end
+
+Fiddle::LIBC_SO = libc_so
+Fiddle::LIBM_SO = libm_so
+
+module Fiddle
+ class TestCase < Test::Unit::TestCase
+ def setup
+ @libc = Fiddle.dlopen(LIBC_SO)
+ @libm = Fiddle.dlopen(LIBM_SO)
+ end
+
+ def teardown
+ if /linux/ =~ RUBY_PLATFORM
+ GC.start
+ end
+ end
+ end
+end
diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb
new file mode 100644
index 0000000000..8ece438f54
--- /dev/null
+++ b/test/fiddle/test_c_struct_entry.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+ require 'fiddle/struct'
+rescue LoadError
+end
+
+module Fiddle
+ class TestCStructEntity < TestCase
+ def test_class_size
+ types = [TYPE_DOUBLE, TYPE_CHAR]
+
+ size = CStructEntity.size types
+
+ alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
+
+ expected = PackInfo.align 0, alignments[0]
+ expected += PackInfo::SIZE_MAP[TYPE_DOUBLE]
+
+ expected = PackInfo.align expected, alignments[1]
+ expected += PackInfo::SIZE_MAP[TYPE_CHAR]
+
+ expected = PackInfo.align expected, alignments.max
+
+ assert_equal expected, size
+ end
+
+ def test_class_size_with_count
+ size = CStructEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
+
+ types = [TYPE_DOUBLE, TYPE_CHAR]
+ alignments = types.map { |type| PackInfo::ALIGN_MAP[type] }
+
+ expected = PackInfo.align 0, alignments[0]
+ expected += PackInfo::SIZE_MAP[TYPE_DOUBLE] * 2
+
+ expected = PackInfo.align expected, alignments[1]
+ expected += PackInfo::SIZE_MAP[TYPE_CHAR] * 20
+
+ expected = PackInfo.align expected, alignments.max
+
+ assert_equal expected, size
+ end
+
+ def test_set_ctypes
+ union = CStructEntity.malloc [TYPE_INT, TYPE_LONG]
+ union.assign_names %w[int long]
+
+ # this test is roundabout because the stored ctypes are not accessible
+ union['long'] = 1
+ union['int'] = 2
+
+ assert_equal 1, union['long']
+ assert_equal 2, union['int']
+ end
+
+ def test_aref_pointer_array
+ team = CStructEntity.malloc([[TYPE_VOIDP, 2]])
+ team.assign_names(["names"])
+ alice = Fiddle::Pointer.malloc(6)
+ alice[0, 6] = "Alice\0"
+ bob = Fiddle::Pointer.malloc(4)
+ bob[0, 4] = "Bob\0"
+ team["names"] = [alice, bob]
+ assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
+ end
+
+ def test_aref_pointer
+ user = CStructEntity.malloc([TYPE_VOIDP])
+ user.assign_names(["name"])
+ alice = Fiddle::Pointer.malloc(6)
+ alice[0, 6] = "Alice\0"
+ user["name"] = alice
+ assert_equal("Alice", user["name"].to_s)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb
new file mode 100644
index 0000000000..5727a20e3b
--- /dev/null
+++ b/test/fiddle/test_c_union_entity.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+ require 'fiddle/struct'
+rescue LoadError
+end
+
+
+module Fiddle
+ class TestCUnionEntity < TestCase
+ def test_class_size
+ size = CUnionEntity.size([TYPE_DOUBLE, TYPE_CHAR])
+
+ assert_equal SIZEOF_DOUBLE, size
+ end
+
+ def test_class_size_with_count
+ size = CUnionEntity.size([[TYPE_DOUBLE, 2], [TYPE_CHAR, 20]])
+
+ assert_equal SIZEOF_CHAR * 20, size
+ end
+
+ def test_set_ctypes
+ union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG]
+ union.assign_names %w[int long]
+
+ # this test is roundabout because the stored ctypes are not accessible
+ union['long'] = 1
+ assert_equal 1, union['long']
+
+ union['int'] = 1
+ assert_equal 1, union['int']
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_closure.rb b/test/fiddle/test_closure.rb
new file mode 100644
index 0000000000..2de0660725
--- /dev/null
+++ b/test/fiddle/test_closure.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestClosure < Fiddle::TestCase
+ def test_argument_errors
+ assert_raise(TypeError) do
+ Closure.new(TYPE_INT, TYPE_INT)
+ end
+
+ assert_raise(TypeError) do
+ Closure.new('foo', [TYPE_INT])
+ end
+
+ assert_raise(TypeError) do
+ Closure.new(TYPE_INT, ['meow!'])
+ end
+ end
+
+ def test_call
+ closure = Class.new(Closure) {
+ def call
+ 10
+ end
+ }.new(TYPE_INT, [])
+
+ func = Function.new(closure, [], TYPE_INT)
+ assert_equal 10, func.call
+ end
+
+ def test_returner
+ closure = Class.new(Closure) {
+ def call thing
+ thing
+ end
+ }.new(TYPE_INT, [TYPE_INT])
+
+ func = Function.new(closure, [TYPE_INT], TYPE_INT)
+ assert_equal 10, func.call(10)
+ end
+
+ def test_block_caller
+ cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
+ one
+ end
+ func = Function.new(cb, [TYPE_INT], TYPE_INT)
+ assert_equal 11, func.call(11)
+ end
+
+ def test_memsize
+ require 'objspace'
+ bug = '[ruby-dev:42480]'
+ n = 10000
+ assert_equal(n, n.times {ObjectSpace.memsize_of(Closure.allocate)}, bug)
+ end
+
+ %w[INT SHORT CHAR LONG LONG_LONG].each do |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
+
+ clos = Class.new(Closure) do
+ define_method(:call) {|x| arg = x}
+ end.new(t, [t])
+
+ v = ~(~0 << (8*s))
+
+ arg = nil
+ assert_equal(v, clos.call(v))
+ assert_equal(arg, v, n)
+
+ arg = nil
+ func = Function.new(clos, [t], t)
+ assert_equal(v, func.call(v))
+ assert_equal(arg, v, n)
+ end
+ end
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
new file mode 100644
index 0000000000..c053706e13
--- /dev/null
+++ b/test/fiddle/test_cparser.rb
@@ -0,0 +1,211 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+ require 'fiddle/cparser'
+rescue LoadError
+end
+
+module Fiddle
+ class TestCParser < TestCase
+ include CParser
+
+ def test_char_ctype
+ assert_equal(TYPE_CHAR, parse_ctype('char'))
+ assert_equal(TYPE_CHAR, parse_ctype('signed char'))
+ assert_equal(-TYPE_CHAR, parse_ctype('unsigned char'))
+ end
+
+ def test_short_ctype
+ assert_equal(TYPE_SHORT, parse_ctype('short'))
+ assert_equal(TYPE_SHORT, parse_ctype('short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('signed short int'))
+ assert_equal(-TYPE_SHORT, parse_ctype('unsigned short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('unsigned short int'))
+ end
+
+ def test_int_ctype
+ assert_equal(TYPE_INT, parse_ctype('int'))
+ assert_equal(TYPE_INT, parse_ctype('signed int'))
+ assert_equal(-TYPE_INT, parse_ctype('uint'))
+ assert_equal(-TYPE_INT, parse_ctype('unsigned int'))
+ end
+
+ def test_long_ctype
+ assert_equal(TYPE_LONG, parse_ctype('long'))
+ assert_equal(TYPE_LONG, parse_ctype('long int'))
+ assert_equal(TYPE_LONG, parse_ctype('signed long'))
+ assert_equal(TYPE_LONG, parse_ctype('signed long int'))
+ assert_equal(-TYPE_LONG, parse_ctype('unsigned long'))
+ assert_equal(-TYPE_LONG, parse_ctype('unsigned long int'))
+ end
+
+ def test_size_t_ctype
+ assert_equal(TYPE_SIZE_T, parse_ctype("size_t"))
+ end
+
+ def test_ssize_t_ctype
+ assert_equal(TYPE_SSIZE_T, parse_ctype("ssize_t"))
+ end
+
+ def test_ptrdiff_t_ctype
+ assert_equal(TYPE_PTRDIFF_T, parse_ctype("ptrdiff_t"))
+ end
+
+ def test_intptr_t_ctype
+ assert_equal(TYPE_INTPTR_T, parse_ctype("intptr_t"))
+ end
+
+ def test_uintptr_t_ctype
+ assert_equal(TYPE_UINTPTR_T, parse_ctype("uintptr_t"))
+ end
+
+ def test_undefined_ctype
+ assert_raise(DLError) { parse_ctype('DWORD') }
+ end
+
+ def test_undefined_ctype_with_type_alias
+ assert_equal(-TYPE_LONG, parse_ctype('DWORD', {"DWORD" => "unsigned long"}))
+ end
+
+ def test_struct_basic
+ assert_equal [[TYPE_INT, TYPE_CHAR], ['i', 'c']], parse_struct_signature(['int i', 'char c'])
+ end
+
+ def test_struct_array
+ assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature(['char buffer[80]', 'int[5] x'])
+ end
+
+ def test_struct_array_str
+ assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature('char buffer[80], int[5] x')
+ end
+
+ def test_struct_function_pointer
+ assert_equal [[TYPE_VOIDP], ['cb']], parse_struct_signature(['void (*cb)(const char*)'])
+ end
+
+ def test_struct_function_pointer_str
+ assert_equal [[TYPE_VOIDP,TYPE_VOIDP], ['cb', 'data']], parse_struct_signature('void (*cb)(const char*), const char* data')
+ end
+
+ def test_struct_string
+ assert_equal [[TYPE_INT,TYPE_VOIDP,TYPE_VOIDP], ['x', 'cb', 'name']], parse_struct_signature('int x; void (*cb)(); const char* name')
+ end
+
+ def test_struct_undefined
+ assert_raise(DLError) { parse_struct_signature(['int i', 'DWORD cb']) }
+ end
+
+ def test_struct_undefined_with_type_alias
+ assert_equal [[TYPE_INT,-TYPE_LONG], ['i', 'cb']], parse_struct_signature(['int i', 'DWORD cb'], {"DWORD" => "unsigned long"})
+ end
+
+ def test_signature_basic
+ func, ret, args = parse_signature('void func()')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_semi
+ func, ret, args = parse_signature('void func();')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_void_arg
+ func, ret, args = parse_signature('void func(void)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_type_args
+ types = [
+ 'char', 'unsigned char',
+ 'short', 'unsigned short',
+ 'int', 'unsigned int',
+ 'long', 'unsigned long',
+ 'long long', 'unsigned long long',
+ 'float', 'double',
+ 'const char*', 'void*',
+ ]
+ func, ret, args = parse_signature("void func(#{types.join(',')})")
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [
+ TYPE_CHAR, -TYPE_CHAR,
+ TYPE_SHORT, -TYPE_SHORT,
+ TYPE_INT, -TYPE_INT,
+ TYPE_LONG, -TYPE_LONG,
+ TYPE_LONG_LONG, -TYPE_LONG_LONG,
+ TYPE_FLOAT, TYPE_DOUBLE,
+ TYPE_VOIDP, TYPE_VOIDP,
+ ], args
+ end
+
+ def test_signature_single_variable
+ func, ret, args = parse_signature('void func(int x)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_INT], args
+ end
+
+ def test_signature_multiple_variables
+ func, ret, args = parse_signature('void func(int x, const char* s)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_INT, TYPE_VOIDP], args
+ end
+
+ def test_signature_array_variable
+ func, ret, args = parse_signature('void func(int x[], int y[40])')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_VOIDP, TYPE_VOIDP], args
+ end
+
+ def test_signature_function_pointer
+ func, ret, args = parse_signature('int func(int (*sum)(int x, int y), int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_INT, ret
+ assert_equal [TYPE_VOIDP, TYPE_INT, TYPE_INT], args
+ end
+
+ def test_signature_return_pointer
+ func, ret, args = parse_signature('void* malloc(size_t)')
+ assert_equal 'malloc', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_SIZE_T], args
+ end
+
+ def test_signature_return_array
+ func, ret, args = parse_signature('int (*func())[32]')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [], args
+ end
+
+ def test_signature_return_array_with_args
+ func, ret, args = parse_signature('int (*func(const char* s))[]')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_VOIDP], args
+ end
+
+ def test_signature_return_function_pointer
+ func, ret, args = parse_signature('int (*func())(int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [], args
+ end
+
+ def test_signature_return_function_pointer_with_args
+ func, ret, args = parse_signature('int (*func(int z))(int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_INT], args
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_fiddle.rb b/test/fiddle/test_fiddle.rb
new file mode 100644
index 0000000000..8751d96920
--- /dev/null
+++ b/test/fiddle/test_fiddle.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+class TestFiddle < Fiddle::TestCase
+ def test_windows_constant
+ require 'rbconfig'
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+ assert Fiddle::WINDOWS, "Fiddle::WINDOWS should be 'true' on Windows platforms"
+ else
+ refute Fiddle::WINDOWS, "Fiddle::WINDOWS should be 'false' on non-Windows platforms"
+ end
+ end
+
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_func.rb b/test/fiddle/test_func.rb
new file mode 100644
index 0000000000..8c35833a32
--- /dev/null
+++ b/test/fiddle/test_func.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+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_syscall_with_tainted_string
+ f = Function.new(@libc['system'], [TYPE_VOIDP], TYPE_INT)
+ Thread.new {
+ $SAFE = 1
+ assert_raise(SecurityError) do
+ f.call("uname -rs".dup.taint)
+ end
+ }.join
+ 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 if defined?(Fiddle)
diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb
new file mode 100644
index 0000000000..cbf84eae9d
--- /dev/null
+++ b/test/fiddle/test_function.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestFunction < Fiddle::TestCase
+ def setup
+ super
+ Fiddle.last_error = nil
+ end
+
+ def test_default_abi
+ func = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
+ 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_argument_errors
+ assert_raise(TypeError) do
+ Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE)
+ end
+
+ assert_raise(TypeError) do
+ Function.new(@libm['sin'], ['foo'], TYPE_DOUBLE)
+ end
+
+ assert_raise(TypeError) do
+ Function.new(@libm['sin'], [TYPE_DOUBLE], 'foo')
+ end
+ 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
+ end
+
+ def test_argument_count
+ closure = Class.new(Closure) {
+ def call one
+ 10 + one
+ end
+ }.new(TYPE_INT, [TYPE_INT])
+ func = Function.new(closure, [TYPE_INT], TYPE_INT)
+
+ assert_raise(ArgumentError) do
+ func.call(1,2,3)
+ end
+ assert_raise(ArgumentError) do
+ func.call
+ end
+ end
+
+ def test_last_error
+ func = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
+
+ assert_nil Fiddle.last_error
+ func.call(+"000", "123")
+ refute_nil Fiddle.last_error
+ end
+
+ def test_strcpy
+ 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)
+ end
+
+ def test_nogvl_poll
+ begin
+ poll = @libc['poll']
+ rescue Fiddle::DLError
+ skip '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, 100, 'slept correct amount of time')
+ assert_equal(0, n1, 'poll(2) called correctly main-thread')
+ assert_equal(0, n2, 'poll(2) called correctly in sub-thread')
+ end
+
+ def test_no_memory_leak
+ prep = 'r = Fiddle::Function.new(Fiddle.dlopen(nil)["rb_obj_tainted"], [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
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_handle.rb b/test/fiddle/test_handle.rb
new file mode 100644
index 0000000000..77559eb4d9
--- /dev/null
+++ b/test/fiddle/test_handle.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestHandle < TestCase
+ include Fiddle
+
+ def test_safe_handle_open
+ Thread.new do
+ $SAFE = 1
+ assert_raise(SecurityError) {
+ Fiddle::Handle.new(LIBC_SO.dup.taint)
+ }
+ end.join
+ end
+
+ def test_safe_function_lookup
+ Thread.new do
+ h = Fiddle::Handle.new(LIBC_SO)
+ $SAFE = 1
+ assert_raise(SecurityError) {
+ h["qsort".dup.taint]
+ }
+ end.join
+ end
+
+ def test_to_i
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_kind_of Integer, handle.to_i
+ end
+
+ def test_static_sym_unknown
+ assert_raise(DLError) { Fiddle::Handle.sym('fooo') }
+ assert_raise(DLError) { Fiddle::Handle['fooo'] }
+ end
+
+ def test_static_sym
+ begin
+ # Linux / Darwin / FreeBSD
+ refute_nil Fiddle::Handle.sym('dlopen')
+ assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
+ return
+ rescue
+ end
+
+ begin
+ # NetBSD
+ require '-test-/dln/empty'
+ refute_nil Fiddle::Handle.sym('Init_empty')
+ assert_equal Fiddle::Handle.sym('Init_empty'), Fiddle::Handle['Init_empty']
+ return
+ rescue
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_sym_closed_handle
+ handle = Fiddle::Handle.new(LIBC_SO)
+ handle.close
+ assert_raise(DLError) { handle.sym("calloc") }
+ assert_raise(DLError) { handle["calloc"] }
+ end
+
+ def test_sym_unknown
+ handle = Fiddle::Handle.new(LIBC_SO)
+ assert_raise(DLError) { handle.sym('fooo') }
+ assert_raise(DLError) { handle['fooo'] }
+ end
+
+ def test_sym_with_bad_args
+ handle = Handle.new(LIBC_SO)
+ assert_raise(TypeError) { handle.sym(nil) }
+ assert_raise(TypeError) { handle[nil] }
+ 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_raise(DLError) do
+ handle.close
+ end
+ end
+
+ def test_dlopen_returns_handle
+ assert_instance_of Handle, dlopen(LIBC_SO)
+ 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']
+ return
+ rescue
+ end
+
+ begin
+ # 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 '-test-/dln/empty'
+ handle = Handle::NEXT
+ refute_nil handle['Init_empty']
+ return
+ rescue
+ end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_DEFAULT
+ 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("/lib/libc.so.7").sym('strcpy')
+ end if /freebsd/=~ RUBY_PLATFORM
+
+ def test_no_memory_leak
+ assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Handle.allocate}; GC.start', rss: true)
+ end
+
+ if /cygwin|mingw|mswin/ =~ RUBY_PLATFORM
+ def test_fallback_to_ansi
+ k = Fiddle::Handle.new("kernel32.dll")
+ ansi = k["GetFileAttributesA"]
+ assert_equal(ansi, k["GetFileAttributes"], "should fallback to ANSI version")
+ end
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb
new file mode 100644
index 0000000000..ff16d17d50
--- /dev/null
+++ b/test/fiddle/test_import.rb
@@ -0,0 +1,151 @@
+# coding: US-ASCII
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+ require 'fiddle/import'
+rescue LoadError
+end
+
+module Fiddle
+ module LIBC
+ extend Importer
+ dlload LIBC_SO, LIBM_SO
+
+ typealias 'string', 'char*'
+ typealias 'FILE*', 'void*'
+
+ extern "void *strcpy(char*, char*)"
+ extern "int isdigit(int)"
+ extern "double atof(string)"
+ extern "unsigned long strtoul(char*, char **, int)"
+ extern "int qsort(void*, unsigned long, unsigned long, void*)"
+ extern "int fprintf(FILE*, char*)" rescue nil
+ extern "int gettimeofday(timeval*, timezone*)" rescue nil
+
+ BoundQsortCallback = bind("void *bound_qsort_callback(void*, void*)"){|ptr1,ptr2| ptr1[0] <=> ptr2[0]}
+ Timeval = struct [
+ "long tv_sec",
+ "long tv_usec",
+ ]
+ Timezone = struct [
+ "int tz_minuteswest",
+ "int tz_dsttime",
+ ]
+ MyStruct = struct [
+ "short num[5]",
+ "char c",
+ "unsigned char buff[7]",
+ ]
+
+ CallCallback = bind("void call_callback(void*, void*)"){ | ptr1, ptr2|
+ f = Function.new(ptr1.to_i, [TYPE_VOIDP], TYPE_VOID)
+ f.call(ptr2)
+ }
+ end
+
+ class TestImport < TestCase
+ def test_ensure_call_dlload
+ err = assert_raise(RuntimeError) do
+ Class.new do
+ extend Importer
+ extern "void *strcpy(char*, char*)"
+ end
+ end
+ assert_match(/call dlload before/, err.message)
+ end
+
+ def test_malloc()
+ s1 = LIBC::Timeval.malloc()
+ s2 = LIBC::Timeval.malloc()
+ refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
+ end
+
+ def test_sizeof()
+ assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
+ assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
+ assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct.malloc()))
+ assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long"))
+ end
+
+ Fiddle.constants.grep(/\ATYPE_(?!VOID\z)(.*)/) do
+ type = $&
+ size = Fiddle.const_get("SIZEOF_#{$1}")
+ name = $1.sub(/P\z/,"*").gsub(/_(?!T\z)/, " ").downcase
+ define_method("test_sizeof_#{name}") do
+ assert_equal(size, Fiddle::Importer.sizeof(name), type)
+ end
+ end
+
+ def test_unsigned_result()
+ d = (2 ** 31) + 1
+
+ r = LIBC.strtoul(d.to_s, 0, 0)
+ assert_equal(d, r)
+ end
+
+ def test_io()
+ if( RUBY_PLATFORM != BUILD_RUBY_PLATFORM ) || !defined?(LIBC.fprintf)
+ return
+ end
+ io_in,io_out = IO.pipe()
+ LIBC.fprintf(io_out, "hello")
+ io_out.flush()
+ io_out.close()
+ str = io_in.read()
+ io_in.close()
+ assert_equal("hello", str)
+ end
+
+ def test_value()
+ i = LIBC.value('int', 2)
+ assert_equal(2, i.value)
+
+ d = LIBC.value('double', 2.0)
+ assert_equal(2.0, d.value)
+
+ ary = LIBC.value('int[3]', [0,1,2])
+ assert_equal([0,1,2], ary.value)
+ end
+
+ def test_struct()
+ s = LIBC::MyStruct.malloc()
+ s.num = [0,1,2,3,4]
+ s.c = ?a.ord
+ s.buff = "012345\377"
+ assert_equal([0,1,2,3,4], s.num)
+ assert_equal(?a.ord, s.c)
+ assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
+ end
+
+ def test_gettimeofday()
+ if( defined?(LIBC.gettimeofday) )
+ timeval = LIBC::Timeval.malloc()
+ timezone = LIBC::Timezone.malloc()
+ LIBC.gettimeofday(timeval, timezone)
+ cur = Time.now()
+ assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
+ end
+ end
+
+ def test_strcpy()
+ buff = +"000"
+ str = LIBC.strcpy(buff, "123")
+ assert_equal("123", buff)
+ assert_equal("123", str.to_s)
+ end
+
+ def test_isdigit
+ r1 = LIBC.isdigit(?1.ord)
+ r2 = LIBC.isdigit(?2.ord)
+ rr = LIBC.isdigit(?r.ord)
+ assert_operator(r1, :>, 0)
+ assert_operator(r2, :>, 0)
+ assert_equal(0, rr)
+ end
+
+ def test_atof
+ r = LIBC.atof("12.34")
+ assert_includes(12.00..13.00, r)
+ end
+ end
+end if defined?(Fiddle)
diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb
new file mode 100644
index 0000000000..27a8a6cb06
--- /dev/null
+++ b/test/fiddle/test_pointer.rb
@@ -0,0 +1,236 @@
+# frozen_string_literal: true
+begin
+ require_relative 'helper'
+rescue LoadError
+end
+
+module Fiddle
+ class TestPointer < TestCase
+ def dlwrap arg
+ Fiddle.dlwrap arg
+ end
+
+ 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 = Marshal.load(Marshal.dump("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 = Marshal.load(Marshal.dump("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_raise(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 = Marshal.load(Marshal.dump('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
+
+ def test_no_memory_leak
+ assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Pointer.allocate}', rss: true)
+ end
+ end
+end if defined?(Fiddle)