diff options
Diffstat (limited to 'spec/ffi')
-rw-r--r-- | spec/ffi/async_callback_spec.rb | 23 | ||||
-rw-r--r-- | spec/ffi/dynamic_library_spec.rb | 60 | ||||
-rw-r--r-- | spec/ffi/enum_spec.rb | 38 | ||||
-rw-r--r-- | spec/ffi/errno_spec.rb | 8 | ||||
-rw-r--r-- | spec/ffi/fixtures/compile.rb | 2 | ||||
-rw-r--r-- | spec/ffi/function_spec.rb | 24 | ||||
-rw-r--r-- | spec/ffi/function_type_spec.rb | 2 | ||||
-rw-r--r-- | spec/ffi/library_spec.rb | 100 | ||||
-rw-r--r-- | spec/ffi/memorypointer_spec.rb | 34 | ||||
-rw-r--r-- | spec/ffi/platform_spec.rb | 13 | ||||
-rw-r--r-- | spec/ffi/pointer_spec.rb | 12 | ||||
-rw-r--r-- | spec/ffi/rbx/memory_pointer_spec.rb | 12 | ||||
-rw-r--r-- | spec/ffi/spec_helper.rb | 1 | ||||
-rw-r--r-- | spec/ffi/struct_by_ref_spec.rb | 9 | ||||
-rw-r--r-- | spec/ffi/struct_spec.rb | 64 | ||||
-rw-r--r-- | spec/ffi/type_spec.rb | 35 | ||||
-rw-r--r-- | spec/ffi/variadic_spec.rb | 32 |
17 files changed, 434 insertions, 35 deletions
diff --git a/spec/ffi/async_callback_spec.rb b/spec/ffi/async_callback_spec.rb index 51a4886..7bb9f70 100644 --- a/spec/ffi/async_callback_spec.rb +++ b/spec/ffi/async_callback_spec.rb @@ -50,4 +50,27 @@ describe "async callback" do expect(callback_runner_thread.name).to eq("FFI Callback Runner") end + + it "works in Ractor", :ractor do + skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby" + + res = Ractor.new do + v = 0xdeadbeef + correct_ractor = false + correct_thread = false + thread = Thread.current + rac = Ractor.current + cb = Proc.new do |i| + v = i + correct_ractor = rac == Ractor.current + correct_thread = thread != Thread.current + end + LibTest.testAsyncCallback(cb, 0x7fffffff) + + [v, correct_ractor, correct_thread] + end.take + + expect(res).to eq([0x7fffffff, true, true]) + end + end diff --git a/spec/ffi/dynamic_library_spec.rb b/spec/ffi/dynamic_library_spec.rb new file mode 100644 index 0000000..088a822 --- /dev/null +++ b/spec/ffi/dynamic_library_spec.rb @@ -0,0 +1,60 @@ +# +# 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::DynamicLibrary do + it "should be shareable for Ractor", :ractor do + libtest = FFI::DynamicLibrary.open(TestLibrary::PATH, + FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) + + res = Ractor.new(libtest) do |libtest2| + libtest2.find_symbol("testClosureVrV").address + end.take + + expect( res ).to be > 0 + end + + it "load a library in a Ractor", :ractor do + res = Ractor.new do + libtest = FFI::DynamicLibrary.open(TestLibrary::PATH, + FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) + libtest.find_symbol("testClosureVrV") + end.take + + expect(res.address).to be > 0 + end + + it "has a memsize function", skip: RUBY_ENGINE != "ruby" do + base_size = ObjectSpace.memsize_of(Object.new) + + libtest = FFI::DynamicLibrary.open(TestLibrary::PATH, + FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) + size = ObjectSpace.memsize_of(libtest) + expect(size).to be > base_size + end + + describe Symbol do + before do + @libtest = FFI::DynamicLibrary.open( + TestLibrary::PATH, + FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL, + ) + end + + it "has a memsize function", skip: RUBY_ENGINE != "ruby" do + base_size = ObjectSpace.memsize_of(Object.new) + + symbol = @libtest.find_symbol("gvar_gstruct_set") + size = ObjectSpace.memsize_of(symbol) + expect(size).to be > base_size + end + + it "should be shareable for Ractor", :ractor do + symbol = @libtest.find_symbol("gvar_gstruct_set") + expect(Ractor.shareable?(symbol)).to be true + end + end +end diff --git a/spec/ffi/enum_spec.rb b/spec/ffi/enum_spec.rb index b8c5b57..7a55b07 100644 --- a/spec/ffi/enum_spec.rb +++ b/spec/ffi/enum_spec.rb @@ -26,14 +26,18 @@ module TestEnum3 ffi_lib TestLibrary::PATH enum :enum_type1, [:c1, :c2, :c3, :c4] - enum :enum_type2, [:c5, 42, :c6, :c7, :c8] - enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12] - enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242] - attach_function :test_tagged_typedef_enum1, [:enum_type1], :enum_type1 + + enum :enum_type2, [:c5, 42, :c6, :c7, :c8] attach_function :test_tagged_typedef_enum2, [:enum_type2], :enum_type2 + + enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12] attach_function :test_tagged_typedef_enum3, [:enum_type3], :enum_type3 + + enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242] attach_function :test_tagged_typedef_enum4, [:enum_type4], :enum_type4 + + freeze end module TestEnum4 @@ -44,18 +48,22 @@ module TestEnum4 enum :enum_type1, [:c5, 0x42, :c6, :c7, :c8] enum :enum_type2, [:c9, 0x42, :c10, :c11, 0x4242, :c12] enum :enum_type3, [:c13, 0x42, :c14, 0x4242, :c15, 0x42424242, :c16, 0x4242424242424242] - enum FFI::Type::UINT16, :enum_type4, [:c17, 0x42, :c18, :c19, :c20] - enum FFI::Type::UINT32, :enum_type5, [:c21, 0x42, :c22, :c23, 0x4242, :c24] - enum FFI::Type::UINT64, :enum_type6, [:c25, 0x42, :c26, 0x4242, :c27, 0x42424242, :c28, 0x4242424242424242] - enum FFI::Type::UINT64, [:c29, 0x4242424242424242, :c30, :c31, :c32] attach_function :test_untagged_nonint_enum, [:uint8], :uint8 attach_function :test_tagged_nonint_enum1, [:uint16], :uint16 attach_function :test_tagged_nonint_enum2, [:uint32], :uint32 attach_function :test_tagged_nonint_enum3, [:uint64], :uint64 + + enum FFI::Type::UINT16, :enum_type4, [:c17, 0x42, :c18, :c19, :c20] + enum FFI::Type::UINT32, :enum_type5, [:c21, 0x42, :c22, :c23, 0x4242, :c24] + enum FFI::Type::UINT64, :enum_type6, [:c25, 0x42, :c26, 0x4242, :c27, 0x42424242, :c28, 0x4242424242424242] + enum FFI::Type::UINT64, [:c29, 0x4242424242424242, :c30, :c31, :c32] + attach_function :test_tagged_nonint_enum4, :test_tagged_nonint_enum1, [:enum_type4], :enum_type4 attach_function :test_tagged_nonint_enum5, :test_tagged_nonint_enum2, [:enum_type5], :enum_type5 attach_function :test_tagged_nonint_enum6, :test_tagged_nonint_enum3, [:enum_type6], :enum_type6 + + freeze end describe "A library with no enum defined" do @@ -430,4 +438,18 @@ describe "All enums" do end end.to raise_error(ArgumentError, /duplicate/) end + + it "should be usable in Ractor", :ractor do + res = Ractor.new do + [ + TestEnum1.test_untagged_enum(:c1), + TestEnum3.test_tagged_typedef_enum1(:c1), + TestEnum4.test_tagged_nonint_enum4(0x45), + TestEnum3.enum_type(:enum_type1)[0], + TestEnum4.enum_type(:enum_type6)[0x4242424242424242], + TestEnum4.enum_value(:c3) + ] + end.take + expect( res ).to eq( [0, :c1, :c20, :c1, :c28, 2] ) + end end diff --git a/spec/ffi/errno_spec.rb b/spec/ffi/errno_spec.rb index 4170f92..c0207b8 100644 --- a/spec/ffi/errno_spec.rb +++ b/spec/ffi/errno_spec.rb @@ -34,4 +34,12 @@ describe "FFI.errno" do expect(FFI.errno).to eq(0x12345678) end end + + it "works in Ractor", :ractor do + res = Ractor.new do + LibTest.setLastError(0x12345678) + FFI.errno + end.take + expect(res).to eq(0x12345678) + end end diff --git a/spec/ffi/fixtures/compile.rb b/spec/ffi/fixtures/compile.rb index 58ee561..2be97cb 100644 --- a/spec/ffi/fixtures/compile.rb +++ b/spec/ffi/fixtures/compile.rb @@ -69,5 +69,5 @@ module TestLibrary lib end - PATH = compile_library(".", "libtest.#{FFI::Platform::LIBSUFFIX}") + PATH = FFI.make_shareable(compile_library(".", "libtest.#{FFI::Platform::LIBSUFFIX}")) end diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index 7f90ea3..77d09d8 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -48,6 +48,30 @@ describe FFI::Function do expect(LibTest.testFunctionAdd(10, 10, function_add)).to eq(20) end + def adder(a, b) + a + b + end + + it "can be made shareable for Ractor", :ractor do + add = FFI::Function.new(:int, [:int, :int], &method(:adder)) + Ractor.make_shareable(add) + + res = Ractor.new(add) do |add2| + LibTest.testFunctionAdd(10, 10, add2) + end.take + + expect( res ).to eq(20) + end + + it "should be usable with Ractor", :ractor do + res = Ractor.new do + function_add = FFI::Function.new(:int, [:int, :int]) { |a, b| a + b } + LibTest.testFunctionAdd(10, 10, function_add) + end.take + + expect( res ).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 diff --git a/spec/ffi/function_type_spec.rb b/spec/ffi/function_type_spec.rb index 54e1c48..2f171d8 100644 --- a/spec/ffi/function_type_spec.rb +++ b/spec/ffi/function_type_spec.rb @@ -8,7 +8,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) describe "FFI::FunctionType", skip: RUBY_ENGINE != "ruby" do it 'is initialized with return type and a list of parameter types' do function_type = FFI::FunctionType.new(:int, [ :char, :ulong ]) - expect(function_type.result_type).to be == FFI::Type::Builtin::INT + expect(function_type.return_type).to be == FFI::Type::Builtin::INT expect(function_type.param_types).to be == [ FFI::Type::Builtin::CHAR, FFI::Type::Builtin::ULONG ] end diff --git a/spec/ffi/library_spec.rb b/spec/ffi/library_spec.rb index a17d673..8691b17 100644 --- a/spec/ffi/library_spec.rb +++ b/spec/ffi/library_spec.rb @@ -5,6 +5,12 @@ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +module TestEnumValueRactor + extend FFI::Library + enum :something, [:one, :two] + freeze +end + describe "Library" do describe ".enum_value" do m = Module.new do @@ -20,6 +26,14 @@ describe "Library" do it "should return nil for an invalid key" do expect(m.enum_value(:three)).to be nil end + + it "should be queryable in Ractor", :ractor do + res = Ractor.new do + TestEnumValueRactor.enum_value(:one) + end.take + + expect( res ).to eq(0) + end end describe "#ffi_convention" do @@ -184,15 +198,49 @@ describe "Library" do end.getpid).to eq(Process.pid) }.to raise_error(LoadError) end + end - it "attach_function :bool_return_true from [ File.expand_path(#{TestLibrary::PATH.inspect}) ]" do - mod = Module.new do |m| - m.extend FFI::Library - ffi_lib File.expand_path(TestLibrary::PATH) - attach_function :bool_return_true, [ ], :bool - end - expect(mod.bool_return_true).to be true + it "attach_function :bool_return_true from [ File.expand_path(#{TestLibrary::PATH.inspect}) ]" do + mod = Module.new do |m| + m.extend FFI::Library + ffi_lib File.expand_path(TestLibrary::PATH) + attach_function :bool_return_true, [ ], :bool + end + expect(mod.bool_return_true).to be true + end + + it "can define a foo! function" do + mod = Module.new do |m| + m.extend FFI::Library + ffi_lib File.expand_path(TestLibrary::PATH) + attach_function :foo!, :bool_return_true, [], :bool end + expect(mod.foo!).to be true + end + + it "can define a foo? function" do + mod = Module.new do |m| + m.extend FFI::Library + ffi_lib File.expand_path(TestLibrary::PATH) + attach_function :foo?, :bool_return_true, [], :bool + end + expect(mod.foo?).to be true + end + + it "can reveal the function type" do + skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby' + skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby' + + mod = Module.new do |m| + m.extend FFI::Library + ffi_lib File.expand_path(TestLibrary::PATH) + attach_function :bool_return_true, [ :string ], :bool + end + + fun = mod.attached_functions + expect(fun.keys).to eq([:bool_return_true]) + expect(fun[:bool_return_true].param_types).to eq([FFI::Type::STRING]) + expect(fun[:bool_return_true].return_type).to eq(FFI::Type::BOOL) end def gvar_lib(name, type) @@ -323,20 +371,34 @@ describe "Library" do end end - describe "Symbol" do - before do - @libtest = FFI::DynamicLibrary.open( - TestLibrary::PATH, - FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL, - ) + it "can reveal its attached global struct based variables" do + lib = Module.new do |m| + m.extend FFI::Library + ffi_lib TestLibrary::PATH + attach_variable :gvari, "gvar_gstruct", GlobalStruct end + expect(lib.attached_variables).to eq({ gvari: GlobalStruct }) + end - it "has a memsize function", skip: RUBY_ENGINE != "ruby" do - base_size = ObjectSpace.memsize_of(Object.new) - - symbol = @libtest.find_symbol("gvar_gstruct_set") - size = ObjectSpace.memsize_of(symbol) - expect(size).to be > base_size + it "can reveal its attached global variables" do + lib = Module.new do |m| + m.extend FFI::Library + ffi_lib TestLibrary::PATH + attach_variable "gvaro", "gvar_u32", :uint32 end + expect(lib.attached_variables).to eq({ gvaro: FFI::Type::UINT32 }) + end + + it "should have shareable constants for Ractor", :ractor do + res = Ractor.new do + [ + FFI::Library::LIBC, + FFI::Library::CURRENT_PROCESS, + FFI::CURRENT_PROCESS, + FFI::USE_THIS_PROCESS_AS_LIBRARY, + ] + end.take + + expect( res.size ).to be > 0 end end diff --git a/spec/ffi/memorypointer_spec.rb b/spec/ffi/memorypointer_spec.rb index c8d1b42..f29e2cb 100644 --- a/spec/ffi/memorypointer_spec.rb +++ b/spec/ffi/memorypointer_spec.rb @@ -28,6 +28,21 @@ describe "MemoryPointer#total" do expect(MemoryPointer.new(1024).total).to eq 1024 end end +describe "MemoryPointer#clear" do + it "should clear the memory" do + ptr = MemoryPointer.new(:long) + ptr.write_long 1234 + expect(ptr.read_long).to eq(1234) + ptr.clear + expect(ptr.read_long).to eq(0) + end + it "should deny changes when frozen" do + skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby" + skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby" + ptr = MemoryPointer.new(:long).freeze + expect{ ptr.clear }.to raise_error(RuntimeError, /memory write/) + end +end describe "MemoryPointer#read_array_of_long" do it "foo" do ptr = MemoryPointer.new(:long, 1024) @@ -76,3 +91,22 @@ describe "MemoryPointer return value" do expect(Stdio.fclose(fp)).to eq 0 unless fp.nil? or fp.null? end end +describe "#autorelease" do + it "should be true by default" do + expect(MemoryPointer.new(8).autorelease?).to be true + end + + it "should return false when autorelease=(false)" do + ptr = MemoryPointer.new(8) + ptr.autorelease = false + expect(ptr.autorelease?).to be false + ptr.free + end + + it "should deny changes when frozen" do + skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby" + skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby" + ptr = MemoryPointer.new(8).freeze + expect{ ptr.autorelease = false }.to raise_error(FrozenError) + end +end diff --git a/spec/ffi/platform_spec.rb b/spec/ffi/platform_spec.rb index ad23621..8890b07 100644 --- a/spec/ffi/platform_spec.rb +++ b/spec/ffi/platform_spec.rb @@ -134,4 +134,17 @@ describe "FFI::Platform.unix?" do expect(FFI::Platform::BYTE_ORDER).to eq(order) end end + + it "should have shareable constants for Ractor", :ractor do + res = Ractor.new do + [ + FFI::Platform::OS, + FFI::Platform::CPU, + FFI::Platform::ARCH, + FFI::Platform::OS, + ] + end.take + + expect( res.size ).to be > 0 + end end diff --git a/spec/ffi/pointer_spec.rb b/spec/ffi/pointer_spec.rb index 820e144..8716d30 100644 --- a/spec/ffi/pointer_spec.rb +++ b/spec/ffi/pointer_spec.rb @@ -90,6 +90,13 @@ describe "Pointer" do expect(PointerTestLib.ptr_ret_pointer(memory, 0).address).to eq(0xdeadbeef) end + it "#write_pointer frozen object" do + skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby" + skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby" + memory = FFI::MemoryPointer.new(:pointer).freeze + expect{ memory.write_pointer(PointerTestLib.ptr_from_address(0xdeadbeef)) }.to raise_error(RuntimeError, /memory write/) + end + it "#read_array_of_pointer" do values = [0x12345678, 0xfeedf00d, 0xdeadbeef] memory = FFI::MemoryPointer.new :pointer, values.size @@ -364,6 +371,11 @@ describe "AutoPointer" do ptr.autorelease = false expect(ptr.autorelease?).to be false end + + it "should deny changes when frozen" do + ptr = ptr_class.new(FFI::Pointer.new(0xdeadbeef)).freeze + expect{ ptr.autorelease = false }.to raise_error(FrozenError) + end end describe "#type_size" do diff --git a/spec/ffi/rbx/memory_pointer_spec.rb b/spec/ffi/rbx/memory_pointer_spec.rb index 2db89a9..1270c9b 100644 --- a/spec/ffi/rbx/memory_pointer_spec.rb +++ b/spec/ffi/rbx/memory_pointer_spec.rb @@ -104,6 +104,18 @@ describe "MemoryPointer" do expect(m.read FFI::Type::BOOL).to eq(false) end + it "allows definition of a custom typedef" do + FFI.typedef :uint32, :fubar_t + expect(FFI.find_type(:fubar_t)).to eq(FFI::Type::Builtin::UINT32) + end + + it "allows overwriting of a default typedef" do + FFI.typedef :uint32, :char + expect(FFI.find_type(:char)).to eq(FFI::Type::Builtin::UINT32) + ensure + FFI.typedef FFI::Type::Builtin::CHAR, :char + end + it "allows writing a custom typedef" do FFI.typedef :uint, :fubar_t FFI.typedef :size_t, :fubar2_t diff --git a/spec/ffi/spec_helper.rb b/spec/ffi/spec_helper.rb index a0adbed..22d1c47 100644 --- a/spec/ffi/spec_helper.rb +++ b/spec/ffi/spec_helper.rb @@ -9,6 +9,7 @@ require 'objspace' RSpec.configure do |c| c.filter_run_excluding gc_dependent: true unless ENV['FFI_TEST_GC'] == 'true' + c.filter_run_excluding( :ractor ) unless defined?(Ractor) && RUBY_VERSION >= "3.1" end module TestLibrary diff --git a/spec/ffi/struct_by_ref_spec.rb b/spec/ffi/struct_by_ref_spec.rb index 0858423..a775f92 100644 --- a/spec/ffi/struct_by_ref_spec.rb +++ b/spec/ffi/struct_by_ref_spec.rb @@ -39,5 +39,14 @@ describe FFI::Struct, ' by_ref' do expect { @api.struct_test(other_class.new) }.to raise_error(TypeError) end + + it "can reveal the mapped type converter" do + skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby' + skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby' + + param_type = @api.attached_functions[:struct_test].param_types[0] + expect(param_type).to be_a(FFI::Type::Mapped) + expect(param_type.converter).to be_a(FFI::StructByReference) + end end diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb index 0ce5273..ab62da1 100644 --- a/spec/ffi/struct_spec.rb +++ b/spec/ffi/struct_spec.rb @@ -410,6 +410,7 @@ module StructSpecsStructTests s.pointer.put_double(0, 1.0) expect(s.pointer.get_double(0)).to eq(1.0) end + module EnumFields extend FFI::Library TestEnum = enum :test_enum, [:c1, 10, :c2, 20, :c3, 30, :c4, 40] @@ -456,13 +457,24 @@ module StructSpecsStructTests end it "Can have CallbackInfo struct field" do + s = CallbackMember::TestStruct.new + add_proc = lambda { |a, b| a + b } + sub_proc = lambda { |a, b| a - b } + s[:add] = add_proc + s[:sub] = sub_proc + expect(CallbackMember.struct_call_add_cb(s, 40, 2)).to eq(42) + expect(CallbackMember.struct_call_sub_cb(s, 44, 2)).to eq(42) + end + + it "Can use CallbackInfo struct field in Ractor", :ractor do + res = Ractor.new do s = CallbackMember::TestStruct.new - add_proc = lambda { |a, b| a+b } - sub_proc = lambda { |a, b| a-b } + add_proc = lambda { |a, b| a + b } s[:add] = add_proc - s[:sub] = sub_proc - expect(CallbackMember.struct_call_add_cb(s, 40, 2)).to eq(42) - expect(CallbackMember.struct_call_sub_cb(s, 44, 2)).to eq(42) + CallbackMember.struct_call_add_cb(s, 40, 2) + end.take + + expect( res ).to eq(42) end it "Can return its members as a list" do @@ -532,6 +544,34 @@ module StructSpecsStructTests expect(a.members).to eq([:a]) expect(b.members).to eq([:a, :b]) end + + it "can be made shareable for Ractor", :ractor do + a = Class.new(FFI::Struct) do + layout :a, :char + end.new + a[:a] = -34 + Ractor.make_shareable(a) + + res = Ractor.new(a) do |a2| + a2[:a] + end.take + + expect( res ).to eq(-34) + end + + it "should be usable with Ractor", :ractor do + class TestStructRactor < FFI::Struct + layout :i, :int + end + + res = Ractor.new do + s = TestStructRactor.new + s[:i] = 0x14 + LibTest.ptr_ret_int32_t(s, 0) + end.take + + expect( res ).to eq(0x14) + end end end @@ -1069,6 +1109,20 @@ describe "Struct memsize functions", skip: RUBY_ENGINE != "ruby" do size = ObjectSpace.memsize_of(layout[:pointer]) expect(size).to be > base_size end + + it "StructLayout should be shareable with Ractor", :ractor do + kl = Class.new(FFI::Struct) do + layout :ptr, :pointer + end + expect(Ractor.shareable?(kl.layout)).to eq(true) + end + + it "StructField should be shareable with Ractor", :ractor do + kl = Class.new(FFI::Struct) do + layout :ptr, :pointer + end + expect(Ractor.shareable?(kl.layout[:ptr])).to eq(true) + end end diff --git a/spec/ffi/type_spec.rb b/spec/ffi/type_spec.rb index eb48a43..e880c83 100644 --- a/spec/ffi/type_spec.rb +++ b/spec/ffi/type_spec.rb @@ -5,7 +5,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) -describe "FFI::Type" do +describe FFI::Type do it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do base_size = ObjectSpace.memsize_of(Object.new) @@ -40,4 +40,37 @@ describe "FFI::Type" do size = ObjectSpace.memsize_of(FFI::Type::Builtin::CHAR) expect(size).to be > base_size end + + it "should be shareable with Ractor", :ractor do + expect(Ractor.shareable?(FFI::Type.new(5))).to eq(true) + end + + describe :Builtin do + it "should be shareable with Ractor", :ractor do + expect(Ractor.shareable?(FFI::Type::INT32)).to eq(true) + end + end + + describe :Mapped do + it "should be shareable with Ractor", :ractor do + converter = Module.new do + extend FFI::DataConverter + + def self.native_type + FFI::Type::INT32 + end + + def self.to_native(val, ctx) + ToNativeMap[val] + end + + def self.from_native(val, ctx) + FromNativeMap[val] + end + end + expect(Ractor.shareable?(converter)).to eq(true) + type = FFI::Type::Mapped.new(converter) + expect(Ractor.shareable?(type)).to eq(true) + end + end end diff --git a/spec/ffi/variadic_spec.rb b/spec/ffi/variadic_spec.rb index f379ed4..b528c09 100644 --- a/spec/ffi/variadic_spec.rb +++ b/spec/ffi/variadic_spec.rb @@ -23,6 +23,8 @@ describe "Function with variadic arguments" do attach_function :testBlockingClose, [ :pointer ], :void attach_function :testCallbackVrDva, :testClosureVrDva, [ :double, :varargs ], :double attach_function :testCallbackVrILva, :testClosureVrILva, [ :int, :long, :varargs ], :long + + freeze end it "takes enum arguments" do @@ -37,6 +39,15 @@ describe "Function with variadic arguments" do expect(LibTest.pack_varargs2(buf, :c1, "ii", :int, :c3, :int, :c4)).to eq(:c2) end + it "can reveal its return and parameters" do + skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby' + skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby' + + fun = LibTest.attached_functions[:testBlockingWRva] + expect(fun.param_types).to eq([FFI::Type::POINTER, FFI::Type::CHAR, FFI::Type::VARARGS]) + expect(fun.return_type).to eq(FFI::Type::INT8) + end + it 'can wrap a blocking function with varargs' do handle = LibTest.testBlockingOpen expect(handle).not_to be_null @@ -87,12 +98,33 @@ describe "Function with variadic arguments" do expect(LibTest.testCallbackVrDva(3.0, :cbVrD, pr)).to be_within(0.0000001).of(45.0) end + it "can be called as instance method" do + kl = Class.new do + include LibTest + def call + pr = proc { 42.0 } + testCallbackVrDva(3.0, :cbVrD, pr) + end + end + expect(kl.new.call).to be_within(0.0000001).of(45.0) + end + it "call variadic with several callback arguments" do pr1 = proc { |i| i + 1 } pr2 = proc { |l| l + 2 } expect(LibTest.testCallbackVrILva(5, 6, :cbVrI, pr1, :cbVrL, pr2)).to eq(14) end + it "should be usable with Ractor", :ractor do + res = Ractor.new do + pr = proc { 42.0 } + LibTest.testCallbackVrDva(3.0, :cbVrD, pr) + end.take + + expect(res).to be_within(0.0000001).of(45.0) + end + + module Varargs PACK_VALUES = { 'c' => [ 0x12 ], |