From 9b0c36b39032cffff3c62a2b0e1fc38fa429f5ea Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Fri, 11 Dec 2020 09:41:12 +0900 Subject: Import fiddle-1.0.4 (#3860) I don't use tool/sync_default_gem.rb because the last sync was incomplete. Co-authored-by: Hiroshi SHIBATA Co-authored-by: Alan Wu Co-authored-by: sinisterchipmunk Co-authored-by: Sutou Kouhei --- test/fiddle/test_c_struct_entry.rb | 130 ++++++++++++--- test/fiddle/test_c_union_entity.rb | 15 +- test/fiddle/test_cparser.rb | 113 +++++++++++++ test/fiddle/test_function.rb | 14 +- test/fiddle/test_import.rb | 324 ++++++++++++++++++++++++++++++++----- test/fiddle/test_memory_view.rb | 115 +++++++++++++ test/fiddle/test_pointer.rb | 85 +++++++--- 7 files changed, 707 insertions(+), 89 deletions(-) create mode 100644 test/fiddle/test_memory_view.rb (limited to 'test/fiddle') diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb index 33bfee6218..9fd16d7101 100644 --- a/test/fiddle/test_c_struct_entry.rb +++ b/test/fiddle/test_c_struct_entry.rb @@ -43,35 +43,123 @@ module Fiddle end def test_set_ctypes - union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE - union.assign_names %w[int long] + CStructEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |struct| + struct.assign_names %w[int long] - # this test is roundabout because the stored ctypes are not accessible - union['long'] = 1 - union['int'] = 2 + # this test is roundabout because the stored ctypes are not accessible + struct['long'] = 1 + struct['int'] = 2 - assert_equal 1, union['long'] - assert_equal 2, union['int'] + assert_equal 1, struct['long'] + assert_equal 2, struct['int'] + end end def test_aref_pointer_array - team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) - team.assign_names(["names"]) - alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) - alice[0, 6] = "Alice\0" - bob = Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) - bob[0, 4] = "Bob\0" - team["names"] = [alice, bob] - assert_equal(["Alice", "Bob"], team["names"].map(&:to_s)) + CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE) do |team| + team.assign_names(["names"]) + Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| + alice[0, 6] = "Alice\0" + Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE) do |bob| + bob[0, 4] = "Bob\0" + team["names"] = [alice, bob] + assert_equal(["Alice", "Bob"], team["names"].map(&:to_s)) + end + end + end end def test_aref_pointer - user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) - user.assign_names(["name"]) - alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) - alice[0, 6] = "Alice\0" - user["name"] = alice - assert_equal("Alice", user["name"].to_s) + CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE) do |user| + user.assign_names(["name"]) + Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE) do |alice| + alice[0, 6] = "Alice\0" + user["name"] = alice + assert_equal("Alice", user["name"].to_s) + end + end + end + + def test_new_double_free + types = [TYPE_INT] + Pointer.malloc(CStructEntity.size(types), Fiddle::RUBY_FREE) do |pointer| + assert_raise ArgumentError do + CStructEntity.new(pointer, types, Fiddle::RUBY_FREE) + end + end + end + + def test_malloc_block + escaped_struct = nil + returned = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_equal Fiddle::SIZEOF_INT, struct.size + assert_equal Fiddle::RUBY_FREE, struct.free.to_i + escaped_struct = struct + :returned + end + assert_equal :returned, returned + assert escaped_struct.freed? + end + + def test_malloc_block_no_free + assert_raise ArgumentError do + CStructEntity.malloc([TYPE_INT]) { |struct| } + end + end + + def test_free + struct = CStructEntity.malloc([TYPE_INT]) + begin + assert_nil struct.free + ensure + Fiddle.free struct + end + end + + def test_free_with_func + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.freed? + struct.call_free + assert struct.freed? + struct.call_free # you can safely run it again + assert struct.freed? + GC.start # you can safely run the GC routine + assert struct.freed? + end + + def test_free_with_no_func + struct = CStructEntity.malloc([TYPE_INT]) + refute struct.freed? + struct.call_free + refute struct.freed? + struct.call_free # you can safely run it again + refute struct.freed? + end + + def test_freed? + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.freed? + struct.call_free + assert struct.freed? + end + + def test_null? + struct = CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) + refute struct.null? + end + + def test_size + CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_equal Fiddle::SIZEOF_INT, struct.size + end + end + + def test_size= + CStructEntity.malloc([TYPE_INT], Fiddle::RUBY_FREE) do |struct| + assert_raise NoMethodError do + struct.size = 1 + end + end end end end if defined?(Fiddle) diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb index 9310084733..e0a3757562 100644 --- a/test/fiddle/test_c_union_entity.rb +++ b/test/fiddle/test_c_union_entity.rb @@ -21,15 +21,16 @@ module Fiddle end def test_set_ctypes - union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE - union.assign_names %w[int long] + CUnionEntity.malloc([TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE) do |union| + 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'] + # 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'] + union['int'] = 1 + assert_equal 1, union['int'] + end end end end if defined?(Fiddle) diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb index 5d9ac3c815..ef8cec5daa 100644 --- a/test/fiddle/test_cparser.rb +++ b/test/fiddle/test_cparser.rb @@ -2,6 +2,7 @@ begin require_relative 'helper' require 'fiddle/cparser' + require 'fiddle/import' rescue LoadError end @@ -68,6 +69,19 @@ module Fiddle assert_equal(-TYPE_LONG, parse_ctype('DWORD', {"DWORD" => "unsigned long"})) end + def expand_struct_types(types) + types.collect do |type| + case type + when Class + [expand_struct_types(type.types)] + when Array + [expand_struct_types([type[0]])[0][0], type[1]] + else + type + end + end + end + def test_struct_basic assert_equal [[TYPE_INT, TYPE_CHAR], ['i', 'c']], parse_struct_signature(['int i', 'char c']) end @@ -76,6 +90,93 @@ module Fiddle assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature(['char buffer[80]', 'int[5] x']) end + def test_struct_nested_struct + types, members = parse_struct_signature([ + 'int x', + {inner: ['int i', 'char c']}, + ]) + assert_equal([[TYPE_INT, [[TYPE_INT, TYPE_CHAR]]], + ['x', ['inner', ['i', 'c']]]], + [expand_struct_types(types), + members]) + end + + def test_struct_nested_defined_struct + inner = Fiddle::Importer.struct(['int i', 'char c']) + assert_equal([[TYPE_INT, inner], + ['x', ['inner', ['i', 'c']]]], + parse_struct_signature([ + 'int x', + {inner: inner}, + ])) + end + + def test_struct_double_nested_struct + types, members = parse_struct_signature([ + 'int x', + { + outer: [ + 'int y', + {inner: ['int i', 'char c']}, + ], + }, + ]) + assert_equal([[TYPE_INT, [[TYPE_INT, [[TYPE_INT, TYPE_CHAR]]]]], + ['x', ['outer', ['y', ['inner', ['i', 'c']]]]]], + [expand_struct_types(types), + members]) + end + + def test_struct_nested_struct_array + types, members = parse_struct_signature([ + 'int x', + { + 'inner[2]' => [ + 'int i', + 'char c', + ], + }, + ]) + assert_equal([[TYPE_INT, [[TYPE_INT, TYPE_CHAR], 2]], + ['x', ['inner', ['i', 'c']]]], + [expand_struct_types(types), + members]) + end + + def test_struct_double_nested_struct_inner_array + types, members = parse_struct_signature(outer: [ + 'int x', + { + 'inner[2]' => [ + 'int i', + 'char c', + ], + }, + ]) + assert_equal([[[[TYPE_INT, [[TYPE_INT, TYPE_CHAR], 2]]]], + [['outer', ['x', ['inner', ['i', 'c']]]]]], + [expand_struct_types(types), + members]) + end + + def test_struct_double_nested_struct_outer_array + types, members = parse_struct_signature([ + 'int x', + { + 'outer[2]' => { + inner: [ + 'int i', + 'char c', + ], + }, + }, + ]) + assert_equal([[TYPE_INT, [[[[TYPE_INT, TYPE_CHAR]]], 2]], + ['x', ['outer', [['inner', ['i', 'c']]]]]], + [expand_struct_types(types), + members]) + 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 @@ -179,6 +280,18 @@ module Fiddle assert_equal [TYPE_VOIDP, TYPE_INT, TYPE_INT], args end + def test_signature_variadic_arguments + unless Fiddle.const_defined?("TYPE_VARIADIC") + skip "libffi doesn't support variadic arguments" + end + assert_equal([ + "printf", + TYPE_INT, + [TYPE_VOIDP, TYPE_VARIADIC], + ], + parse_signature('int printf(const char *format, ...)')) + end + def test_signature_return_pointer func, ret, args = parse_signature('void* malloc(size_t)') assert_equal 'malloc', func diff --git a/test/fiddle/test_function.rb b/test/fiddle/test_function.rb index 0bb66f9b8d..742615a56b 100644 --- a/test/fiddle/test_function.rb +++ b/test/fiddle/test_function.rb @@ -21,6 +21,17 @@ module Fiddle 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_raise(TypeError) do Function.new(@libm['sin'], TYPE_DOUBLE, TYPE_DOUBLE) @@ -111,8 +122,7 @@ module Fiddle n1 = f.call(nil, 0, msec) n2 = th.value t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - delta = EnvUtil.apply_timeout_scale(180) - assert_in_delta(msec, t1 - t0, delta, 'slept amount of time') + 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 diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb index 4afd8e5562..afa8df9e00 100644 --- a/test/fiddle/test_import.rb +++ b/test/fiddle/test_import.rb @@ -36,6 +36,29 @@ module Fiddle "char c", "unsigned char buff[7]", ] + StructNestedStruct = struct [ + { + "vertices[2]" => { + position: ["float x", "float y", "float z"], + texcoord: ["float u", "float v"] + }, + object: ["int id", "void *user_data"], + }, + "int id" + ] + UnionNestedStruct = union [ + { + keyboard: [ + 'unsigned int state', + 'char key' + ], + mouse: [ + 'unsigned int button', + 'unsigned short x', + 'unsigned short y' + ] + } + ] CallCallback = bind("void call_callback(void*, void*)"){ | ptr1, ptr2| f = Function.new(ptr1.to_i, [TYPE_VOIDP], TYPE_VOID) @@ -56,22 +79,18 @@ module Fiddle def test_struct_memory_access() # check memory operations performed directly on struct - my_struct = Fiddle::Importer.struct(['int id']).malloc - begin + Fiddle::Importer.struct(['int id']).malloc(Fiddle::RUBY_FREE) do |my_struct| my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT assert_equal 0x01010101, my_struct.id my_struct.id = 0 assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT] - ensure - Fiddle.free my_struct.to_ptr end end def test_struct_ptr_array_subscript_multiarg() # check memory operations performed on struct#to_ptr - struct = Fiddle::Importer.struct([ 'int x' ]).malloc - begin + Fiddle::Importer.struct([ 'int x' ]).malloc(Fiddle::RUBY_FREE) do |struct| ptr = struct.to_ptr struct.x = 0x02020202 @@ -79,35 +98,25 @@ module Fiddle ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT assert_equal 0x01010101, struct.x - ensure - Fiddle.free struct.to_ptr end end def test_malloc() - s1 = LIBC::Timeval.malloc() - begin - s2 = LIBC::Timeval.malloc() - begin + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s1| + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |s2| refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i) - ensure - Fiddle.free s2.to_ptr end - ensure - Fiddle.free s1.to_ptr end end def test_sizeof() assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*")) assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct)) - my_struct = LIBC::MyStruct.malloc() - begin + LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |my_struct| assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct)) - ensure - Fiddle.free my_struct.to_ptr end assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG) + assert_equal(LIBC::StructNestedStruct.size(), LIBC.sizeof(LIBC::StructNestedStruct)) end Fiddle.constants.grep(/\ATYPE_(?!VOID|VARIADIC\z)(.*)/) do @@ -158,8 +167,7 @@ module Fiddle end def test_struct_array_assignment() - instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc - begin + Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc(Fiddle::RUBY_FREE) do |instance| instance.stages[0] = 1024 instance.stages[1] = 10 instance.stages[2] = 100 @@ -170,39 +178,279 @@ module Fiddle instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT] assert_raise(IndexError) { instance.stages[-1] = 5 } assert_raise(IndexError) { instance.stages[3] = 5 } - ensure - Fiddle.free instance.to_ptr + end + end + + def test_nested_struct_reusing_other_structs() + position_struct = Fiddle::Importer.struct(['float x', 'float y', 'float z']) + texcoord_struct = Fiddle::Importer.struct(['float u', 'float v']) + vertex_struct = Fiddle::Importer.struct(position: position_struct, texcoord: texcoord_struct) + mesh_struct = Fiddle::Importer.struct([ + { + "vertices[2]" => vertex_struct, + object: [ + "int id", + "void *user_data", + ], + }, + "int id", + ]) + assert_equal LIBC::StructNestedStruct.size, mesh_struct.size + + + keyboard_event_struct = Fiddle::Importer.struct(['unsigned int state', 'char key']) + mouse_event_struct = Fiddle::Importer.struct(['unsigned int button', 'unsigned short x', 'unsigned short y']) + event_union = Fiddle::Importer.union([{ keboard: keyboard_event_struct, mouse: mouse_event_struct}]) + assert_equal LIBC::UnionNestedStruct.size, event_union.size + end + + def test_nested_struct_alignment_is_not_its_size() + inner = Fiddle::Importer.struct(['int x', 'int y', 'int z', 'int w']) + outer = Fiddle::Importer.struct(['char a', { 'nested' => inner }, 'char b']) + outer.malloc(Fiddle::RUBY_FREE) do |instance| + offset = instance.to_ptr.instance_variable_get(:"@offset") + assert_equal Fiddle::SIZEOF_INT * 5, offset.last + assert_equal Fiddle::SIZEOF_INT * 6, outer.size + assert_equal instance.to_ptr.size, outer.size + end + end + + def test_struct_nested_struct_members() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + Fiddle::Pointer.malloc(24, Fiddle::RUBY_FREE) do |user_data| + s.vertices[0].position.x = 1 + s.vertices[0].position.y = 2 + s.vertices[0].position.z = 3 + s.vertices[0].texcoord.u = 4 + s.vertices[0].texcoord.v = 5 + s.vertices[1].position.x = 6 + s.vertices[1].position.y = 7 + s.vertices[1].position.z = 8 + s.vertices[1].texcoord.u = 9 + s.vertices[1].texcoord.v = 10 + s.object.id = 100 + s.object.user_data = user_data + s.id = 101 + assert_equal({ + "vertices" => [ + { + "position" => { + "x" => 1, + "y" => 2, + "z" => 3, + }, + "texcoord" => { + "u" => 4, + "v" => 5, + }, + }, + { + "position" => { + "x" => 6, + "y" => 7, + "z" => 8, + }, + "texcoord" => { + "u" => 9, + "v" => 10, + }, + }, + ], + "object" => { + "id" => 100, + "user_data" => user_data, + }, + "id" => 101, + }, + s.to_h) + end + end + end + + def test_union_nested_struct_members() + LIBC::UnionNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + s.keyboard.state = 100 + s.keyboard.key = 101 + assert_equal(100, s.mouse.button) + refute_equal( 0, s.mouse.x) + end + end + + def test_struct_nested_struct_replace_array_element() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + s.vertices[0].position.x = 5 + + vertex_struct = Fiddle::Importer.struct [{ + position: ["float x", "float y", "float z"], + texcoord: ["float u", "float v"] + }] + vertex_struct.malloc(Fiddle::RUBY_FREE) do |vertex| + vertex.position.x = 100 + s.vertices[0] = vertex + + # make sure element was copied by value, but things like memory address + # should not be changed + assert_equal(100, s.vertices[0].position.x) + refute_equal(vertex.object_id, s.vertices[0].object_id) + refute_equal(vertex.to_ptr, s.vertices[0].to_ptr) + end + end + end + + def test_struct_nested_struct_replace_array_element_nil() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + s.vertices[0].position.x = 5 + s.vertices[0] = nil + assert_equal({ + "position" => { + "x" => 0.0, + "y" => 0.0, + "z" => 0.0, + }, + "texcoord" => { + "u" => 0.0, + "v" => 0.0, + }, + }, + s.vertices[0].to_h) + end + end + + def test_struct_nested_struct_replace_array_element_hash() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + s.vertices[0] = { + position: { + x: 10, + y: 100, + } + } + assert_equal({ + "position" => { + "x" => 10.0, + "y" => 100.0, + "z" => 0.0, + }, + "texcoord" => { + "u" => 0.0, + "v" => 0.0, + }, + }, + s.vertices[0].to_h) + end + end + + def test_struct_nested_struct_replace_entire_array() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + vertex_struct = Fiddle::Importer.struct [{ + position: ["float x", "float y", "float z"], + texcoord: ["float u", "float v"] + }] + + vertex_struct.malloc(Fiddle::RUBY_FREE) do |same0| + vertex_struct.malloc(Fiddle::RUBY_FREE) do |same1| + same = [same0, same1] + same[0].position.x = 1; same[1].position.x = 6 + same[0].position.y = 2; same[1].position.y = 7 + same[0].position.z = 3; same[1].position.z = 8 + same[0].texcoord.u = 4; same[1].texcoord.u = 9 + same[0].texcoord.v = 5; same[1].texcoord.v = 10 + s.vertices = same + assert_equal([ + { + "position" => { + "x" => 1.0, + "y" => 2.0, + "z" => 3.0, + }, + "texcoord" => { + "u" => 4.0, + "v" => 5.0, + }, + }, + { + "position" => { + "x" => 6.0, + "y" => 7.0, + "z" => 8.0, + }, + "texcoord" => { + "u" => 9.0, + "v" => 10.0, + }, + } + ], + s.vertices.collect(&:to_h)) + end + end + end + end + + def test_struct_nested_struct_replace_entire_array_with_different_struct() + LIBC::StructNestedStruct.malloc(Fiddle::RUBY_FREE) do |s| + different_struct_same_size = Fiddle::Importer.struct [{ + a: ['float i', 'float j', 'float k'], + b: ['float l', 'float m'] + }] + + different_struct_same_size.malloc(Fiddle::RUBY_FREE) do |different0| + different_struct_same_size.malloc(Fiddle::RUBY_FREE) do |different1| + different = [different0, different1] + different[0].a.i = 11; different[1].a.i = 16 + different[0].a.j = 12; different[1].a.j = 17 + different[0].a.k = 13; different[1].a.k = 18 + different[0].b.l = 14; different[1].b.l = 19 + different[0].b.m = 15; different[1].b.m = 20 + s.vertices[0][0, s.vertices[0].class.size] = different[0].to_ptr + s.vertices[1][0, s.vertices[1].class.size] = different[1].to_ptr + assert_equal([ + { + "position" => { + "x" => 11.0, + "y" => 12.0, + "z" => 13.0, + }, + "texcoord" => { + "u" => 14.0, + "v" => 15.0, + }, + }, + { + "position" => { + "x" => 16.0, + "y" => 17.0, + "z" => 18.0, + }, + "texcoord" => { + "u" => 19.0, + "v" => 20.0, + }, + } + ], + s.vertices.collect(&:to_h)) + end + end end end def test_struct() - s = LIBC::MyStruct.malloc() - begin + LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |s| 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) - ensure - Fiddle.free s.to_ptr end end def test_gettimeofday() if( defined?(LIBC.gettimeofday) ) - timeval = LIBC::Timeval.malloc() - begin - timezone = LIBC::Timezone.malloc() - begin + LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |timeval| + LIBC::Timezone.malloc(Fiddle::RUBY_FREE) do |timezone| LIBC.gettimeofday(timeval, timezone) - ensure - Fiddle.free timezone.to_ptr end cur = Time.now() assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i) - ensure - Fiddle.free timeval.to_ptr end end end @@ -227,9 +475,5 @@ module Fiddle r = LIBC.atof("12.34") assert_includes(12.00..13.00, r) end - - def test_no_message_with_debug - assert_in_out_err(%w[--debug --disable=gems -rfiddle/import], 'p Fiddle::Importer', ['Fiddle::Importer']) - end end end if defined?(Fiddle) diff --git a/test/fiddle/test_memory_view.rb b/test/fiddle/test_memory_view.rb new file mode 100644 index 0000000000..3c310c2d68 --- /dev/null +++ b/test/fiddle/test_memory_view.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true +begin + require_relative 'helper' +rescue LoadError +end + +begin + require '-test-/memory_view' +rescue LoadError +end + +module Fiddle + class TestMemoryView < TestCase + def setup + skip "MemoryView is unavailable" unless defined? Fiddle::MemoryView + end + + def test_null_ptr + assert_raise(ArgumentError) do + MemoryView.new(Fiddle::NULL) + end + end + + def test_memory_view_from_unsupported_obj + obj = Object.new + assert_raise(ArgumentError) do + MemoryView.new(obj) + end + end + + def test_memory_view_from_pointer + str = Marshal.load(Marshal.dump("hello world")) + ptr = Pointer[str] + mview = MemoryView.new(ptr) + assert_same(ptr, mview.obj) + assert_equal(str.length, mview.length) + assert_equal(true, mview.readonly?) + assert_equal(nil, mview.format) + assert_equal(1, mview.item_size) + assert_equal(1, mview.ndim) + assert_equal(nil, mview.shape) + assert_equal(nil, mview.strides) + assert_equal(nil, mview.sub_offsets) + + codes = str.codepoints + assert_equal(codes, (0...str.length).map {|i| mview[i] }) + end + + def test_memory_view_multi_dimensional + skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils + + buf = [ 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 ].pack("l!*") + shape = [3, 4] + md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, nil) + mview = Fiddle::MemoryView.new(md) + assert_equal(buf.length, mview.length) + assert_equal("l!", mview.format) + assert_equal(Fiddle::SIZEOF_LONG, mview.item_size) + assert_equal(2, mview.ndim) + assert_equal(shape, mview.shape) + assert_equal([Fiddle::SIZEOF_LONG*4, Fiddle::SIZEOF_LONG], mview.strides) + assert_equal(nil, mview.sub_offsets) + assert_equal(1, mview[0, 0]) + assert_equal(4, mview[0, 3]) + assert_equal(6, mview[1, 1]) + assert_equal(10, mview[2, 1]) + end + + def test_memory_view_multi_dimensional_with_strides + skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils + + buf = [ 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 ].pack("l!*") + shape = [2, 8] + strides = [4*Fiddle::SIZEOF_LONG*2, Fiddle::SIZEOF_LONG*2] + md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, strides) + mview = Fiddle::MemoryView.new(md) + assert_equal("l!", mview.format) + assert_equal(Fiddle::SIZEOF_LONG, mview.item_size) + assert_equal(buf.length, mview.length) + assert_equal(2, mview.ndim) + assert_equal(shape, mview.shape) + assert_equal(strides, mview.strides) + assert_equal(nil, mview.sub_offsets) + assert_equal(1, mview[0, 0]) + assert_equal(5, mview[0, 2]) + assert_equal(9, mview[1, 0]) + assert_equal(15, mview[1, 3]) + end + + def test_memory_view_multi_dimensional_with_multiple_members + skip "MemoryViewTestUtils is unavailable" unless defined? MemoryViewTestUtils + + buf = [ 1, 2, 3, 4, 5, 6, 7, 8, + -1, -2, -3, -4, -5, -6, -7, -8].pack("s*") + shape = [2, 4] + strides = [4*Fiddle::SIZEOF_SHORT*2, Fiddle::SIZEOF_SHORT*2] + md = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides) + mview = Fiddle::MemoryView.new(md) + assert_equal("ss", mview.format) + assert_equal(Fiddle::SIZEOF_SHORT*2, mview.item_size) + assert_equal(buf.length, mview.length) + assert_equal(2, mview.ndim) + assert_equal(shape, mview.shape) + assert_equal(strides, mview.strides) + assert_equal(nil, mview.sub_offsets) + assert_equal([1, 2], mview[0, 0]) + assert_equal([5, 6], mview[0, 2]) + assert_equal([-1, -2], mview[1, 0]) + assert_equal([-7, -8], mview[1, 3]) + end + end +end diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index c69e4f7142..e685fea5dc 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -32,6 +32,31 @@ module Fiddle assert_equal free.to_i, ptr.free.to_i end + def test_malloc_block + escaped_ptr = nil + returned = Pointer.malloc(10, Fiddle::RUBY_FREE) do |ptr| + assert_equal 10, ptr.size + assert_equal Fiddle::RUBY_FREE, ptr.free.to_i + escaped_ptr = ptr + :returned + end + assert_equal :returned, returned + assert escaped_ptr.freed? + end + + def test_malloc_block_no_free + assert_raise ArgumentError do + Pointer.malloc(10) { |ptr| } + end + end + + def test_malloc_subclass + subclass = Class.new(Pointer) + subclass.malloc(10, Fiddle::RUBY_FREE) do |ptr| + assert ptr.is_a?(subclass) + end + end + def test_to_str str = Marshal.load(Marshal.dump("hello world")) ptr = Pointer[str] @@ -84,17 +109,18 @@ module Fiddle end def test_to_ptr_io - buf = Pointer.malloc(10, Fiddle::RUBY_FREE) - 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 + Pointer.malloc(10, Fiddle::RUBY_FREE) do |buf| + 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 end @@ -170,27 +196,48 @@ module Fiddle assert_equal free.ptr, ptr.free.ptr end + def test_free_with_func + ptr = Pointer.malloc(4, Fiddle::RUBY_FREE) + refute ptr.freed? + ptr.call_free + assert ptr.freed? + ptr.call_free # you can safely run it again + assert ptr.freed? + GC.start # you can safely run the GC routine + assert ptr.freed? + end + + def test_free_with_no_func + ptr = Pointer.malloc(4) + refute ptr.freed? + ptr.call_free + refute ptr.freed? + ptr.call_free # you can safely run it again + refute ptr.freed? + end + + def test_freed? + ptr = Pointer.malloc(4, Fiddle::RUBY_FREE) + refute ptr.freed? + ptr.call_free + assert ptr.freed? + end + def test_null? ptr = Pointer.new(0) assert ptr.null? end def test_size - ptr = Pointer.malloc(4) - begin + Pointer.malloc(4, Fiddle::RUBY_FREE) do |ptr| assert_equal 4, ptr.size - ensure - Fiddle.free ptr end end def test_size= - ptr = Pointer.malloc(4) - begin + Pointer.malloc(4, Fiddle::RUBY_FREE) do |ptr| ptr.size = 10 assert_equal 10, ptr.size - ensure - Fiddle.free ptr end end -- cgit v1.2.3