diff options
Diffstat (limited to 'test/ruby/test_memory_view.rb')
| -rw-r--r-- | test/ruby/test_memory_view.rb | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/test/ruby/test_memory_view.rb b/test/ruby/test_memory_view.rb new file mode 100644 index 0000000000..d0122ddd59 --- /dev/null +++ b/test/ruby/test_memory_view.rb @@ -0,0 +1,341 @@ +require "-test-/memory_view" +require "rbconfig/sizeof" + +class TestMemoryView < Test::Unit::TestCase + NATIVE_ENDIAN = MemoryViewTestUtils::NATIVE_ENDIAN + LITTLE_ENDIAN = :little_endian + BIG_ENDIAN = :big_endian + + %I(SHORT INT INT16 INT32 INT64 INTPTR LONG LONG_LONG FLOAT DOUBLE).each do |type| + name = :"#{type}_ALIGNMENT" + const_set(name, MemoryViewTestUtils.const_get(name)) + end + + def test_rb_memory_view_register_duplicated + assert_warning(/Duplicated registration of memory view to/) do + MemoryViewTestUtils.register(MemoryViewTestUtils::ExportableString) + end + end + + def test_rb_memory_view_register_nonclass + assert_raise(TypeError) do + MemoryViewTestUtils.register(Object.new) + end + end + + def sizeof(type) + RbConfig::SIZEOF[type.to_s] + end + + def test_rb_memory_view_item_size_from_format + [ + [nil, 1], ['c', 1], ['C', 1], + ['n', 2], ['v', 2], + ['l', 4], ['L', 4], ['N', 4], ['V', 4], ['f', 4], ['e', 4], ['g', 4], + ['q', 8], ['Q', 8], ['d', 8], ['E', 8], ['G', 8], + ['s', sizeof(:short)], ['S', sizeof(:short)], ['s!', sizeof(:short)], ['S!', sizeof(:short)], + ['i', sizeof(:int)], ['I', sizeof(:int)], ['i!', sizeof(:int)], ['I!', sizeof(:int)], + ['l!', sizeof(:long)], ['L!', sizeof(:long)], + ['q!', sizeof('long long')], ['Q!', sizeof('long long')], + ['j', sizeof(:intptr_t)], ['J', sizeof(:intptr_t)], + ].each do |format, expected| + actual, err = MemoryViewTestUtils.item_size_from_format(format) + assert_nil(err) + assert_equal(expected, actual, "rb_memory_view_item_size_from_format(#{format || 'NULL'}) == #{expected}") + end + end + + def test_rb_memory_view_item_size_from_format_composed + actual, = MemoryViewTestUtils.item_size_from_format("ccc") + assert_equal(3, actual) + + actual, = MemoryViewTestUtils.item_size_from_format("c3") + assert_equal(3, actual) + + actual, = MemoryViewTestUtils.item_size_from_format("fd") + assert_equal(12, actual) + + actual, = MemoryViewTestUtils.item_size_from_format("fx2d") + assert_equal(14, actual) + end + + def test_rb_memory_view_item_size_from_format_with_spaces + # spaces should be ignored + actual, = MemoryViewTestUtils.item_size_from_format("f x2 d") + assert_equal(14, actual) + end + + def test_rb_memory_view_item_size_from_format_error + assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccca")) + assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccc4a")) + end + + def test_rb_memory_view_parse_item_format + total_size, members, err = MemoryViewTestUtils.parse_item_format("ccc2f3x2d4q!<") + assert_equal(58, total_size) + assert_nil(err) + assert_equal([ + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1}, + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1}, + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2}, + {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 4, size: 4, repeat: 3}, + {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 18, size: 8, repeat: 4}, + {format: 'q', native_size_p: true, endianness: :little_endian, offset: 50, size: sizeof('long long'), repeat: 1} + ], + members) + end + + def test_rb_memory_view_parse_item_format_with_alignment_signle + [ + ["c", false, NATIVE_ENDIAN, 1, 1, 1], + ["C", false, NATIVE_ENDIAN, 1, 1, 1], + ["s", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1], + ["S", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1], + ["s!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1], + ["S!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1], + ["n", false, :big_endian, INT16_ALIGNMENT, sizeof(:int16_t), 1], + ["v", false, :little_endian, INT16_ALIGNMENT, sizeof(:int16_t), 1], + ["i", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1], + ["I", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1], + ["i!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1], + ["I!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1], + ["l", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1], + ["L", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1], + ["l!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1], + ["L!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1], + ["N", false, :big_endian, INT32_ALIGNMENT, sizeof(:int32_t), 1], + ["V", false, :little_endian, INT32_ALIGNMENT, sizeof(:int32_t), 1], + ["f", false, NATIVE_ENDIAN, FLOAT_ALIGNMENT, sizeof(:float), 1], + ["e", false, :little_endian, FLOAT_ALIGNMENT, sizeof(:float), 1], + ["g", false, :big_endian, FLOAT_ALIGNMENT, sizeof(:float), 1], + ["q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1], + ["Q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1], + ["q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1], + ["Q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1], + ["d", false, NATIVE_ENDIAN, DOUBLE_ALIGNMENT, sizeof(:double), 1], + ["E", false, :little_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1], + ["G", false, :big_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1], + ["j", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1], + ["J", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1], + ].each do |type, native_size_p, endianness, alignment, size, repeat, total_size| + total_size, members, err = MemoryViewTestUtils.parse_item_format("|c#{type}") + assert_nil(err) + + padding_size = alignment - 1 + expected_total_size = 1 + padding_size + size + assert_equal(expected_total_size, total_size) + + expected_result = [ + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1}, + {format: type[0], native_size_p: native_size_p, endianness: endianness, offset: alignment, size: size, repeat: repeat}, + ] + assert_equal(expected_result, members) + end + end + + def alignment_padding(total_size, alignment) + res = total_size % alignment + if res > 0 + alignment - res + else + 0 + end + end + + def test_rb_memory_view_parse_item_format_with_alignment_total_size_with_tail_padding + total_size, _members, err = MemoryViewTestUtils.parse_item_format("|lqc") + assert_nil(err) + + expected_total_size = sizeof(:int32_t) + expected_total_size += alignment_padding(expected_total_size, INT32_ALIGNMENT) + expected_total_size += sizeof(:int64_t) + expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT) + expected_total_size += 1 + expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT) + assert_equal(expected_total_size, total_size) + end + + def test_rb_memory_view_parse_item_format_with_alignment_compound + total_size, members, err = MemoryViewTestUtils.parse_item_format("|ccc2f3x2d4cq!<") + assert_nil(err) + + expected_total_size = 1 + 1 + 1*2 + expected_total_size += alignment_padding(expected_total_size, FLOAT_ALIGNMENT) + expected_total_size += sizeof(:float)*3 + 1*2 + expected_total_size += alignment_padding(expected_total_size, DOUBLE_ALIGNMENT) + expected_total_size += sizeof(:double)*4 + 1 + expected_total_size += alignment_padding(expected_total_size, LONG_LONG_ALIGNMENT) + expected_total_size += sizeof("long long") + assert_equal(expected_total_size, total_size) + + expected_result = [ + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1}, + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1}, + {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2}, + ] + offset = 4 + + res = offset % FLOAT_ALIGNMENT + offset += FLOAT_ALIGNMENT - res if res > 0 + expected_result << {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 4, repeat: 3} + offset += 12 + + offset += 2 # 2x + + res = offset % DOUBLE_ALIGNMENT + offset += DOUBLE_ALIGNMENT - res if res > 0 + expected_result << {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 8, repeat: 4} + offset += 32 + + expected_result << {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 1, repeat: 1} + offset += 1 + + res = offset % LONG_LONG_ALIGNMENT + offset += LONG_LONG_ALIGNMENT - res if res > 0 + expected_result << {format: 'q', native_size_p: true, endianness: :little_endian, offset: offset, size: 8, repeat: 1} + + assert_equal(expected_result, members) + end + + def test_rb_memory_view_extract_item_members + m = MemoryViewTestUtils + assert_equal(1, m.extract_item_members([1].pack("c"), "c")) + assert_equal([1, 2], m.extract_item_members([1, 2].pack("ii"), "ii")) + assert_equal([1, 2, 3], m.extract_item_members([1, 2, 3].pack("cls"), "cls")) + end + + def test_rb_memory_view_extract_item_members_endianness + m = MemoryViewTestUtils + assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S>2")) + assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "n2")) + assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S<2")) + assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "v2")) + assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L>")) + assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "N")) + assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L<")) + assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "V")) + assert_equal(0x0102030405060708, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q>")) + assert_equal(0x0807060504030201, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q<")) + end + + def test_rb_memory_view_extract_item_members_float + m = MemoryViewTestUtils + packed = [1.23].pack("f") + assert_equal(packed.unpack("f")[0], m.extract_item_members(packed, "f")) + end + + def test_rb_memory_view_extract_item_members_float_endianness + m = MemoryViewTestUtils + hi, lo = [1.23].pack("f").unpack("L")[0].divmod(0x10000) + packed = [lo, hi].pack("S*") + assert_equal(packed.unpack("e")[0], m.extract_item_members(packed, "e")) + packed = [hi, lo].pack("S*") + assert_equal(packed.unpack("g")[0], m.extract_item_members(packed, "g")) + end + + def test_rb_memory_view_extract_item_members_doble + m = MemoryViewTestUtils + packed = [1.23].pack("d") + assert_equal(1.23, m.extract_item_members(packed, "d")) + end + + def test_rb_memory_view_extract_item_members_doble_endianness + m = MemoryViewTestUtils + hi, lo = [1.23].pack("d").unpack("Q")[0].divmod(0x10000) + packed = [lo, hi].pack("L*") + assert_equal(packed.unpack("E")[0], m.extract_item_members(packed, "E")) + packed = [hi, lo].pack("L*") + assert_equal(packed.unpack("G")[0], m.extract_item_members(packed, "G")) + end + + def test_rb_memory_view_available_p + es = MemoryViewTestUtils::ExportableString.new("ruby") + assert_equal(true, MemoryViewTestUtils.available?(es)) + es = MemoryViewTestUtils::ExportableString.new(nil) + assert_equal(false, MemoryViewTestUtils.available?(es)) + end + + def test_ref_count_with_exported_object + es = MemoryViewTestUtils::ExportableString.new("ruby") + assert_equal(1, MemoryViewTestUtils.ref_count_while_exporting(es, 1)) + assert_equal(2, MemoryViewTestUtils.ref_count_while_exporting(es, 2)) + assert_equal(10, MemoryViewTestUtils.ref_count_while_exporting(es, 10)) + assert_nil(MemoryViewTestUtils.ref_count_while_exporting(es, 0)) + end + + def test_rb_memory_view_init_as_byte_array + # ExportableString's memory view is initialized by rb_memory_view_init_as_byte_array + es = MemoryViewTestUtils::ExportableString.new("ruby") + memory_view_info = MemoryViewTestUtils.get_memory_view_info(es) + assert_equal({ + obj: es, + byte_size: 4, + readonly: true, + format: nil, + item_size: 1, + ndim: 1, + shape: nil, + strides: nil, + sub_offsets: nil + }, + memory_view_info) + end + + def test_rb_memory_view_get_with_memory_view_unavailable_object + es = MemoryViewTestUtils::ExportableString.new(nil) + memory_view_info = MemoryViewTestUtils.get_memory_view_info(es) + assert_nil(memory_view_info) + end + + def test_rb_memory_view_fill_contiguous_strides + row_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], true) + assert_equal([96, 32, 8], + row_major_strides) + + column_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], false) + assert_equal([8, 16, 48], + column_major_strides) + end + + def test_rb_memory_view_get_item_pointer_single_member + buf = [ 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12 ].pack("l!*") + shape = [3, 4] + mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, nil) + assert_equal(1, mv[[0, 0]]) + assert_equal(4, mv[[0, 3]]) + assert_equal(6, mv[[1, 1]]) + assert_equal(10, mv[[2, 1]]) + end + + def test_rb_memory_view_get_item_pointer_multiple_members + buf = [ 1, 2, 3, 4, 5, 6, 7, 8, + -1, -2, -3, -4, -5, -6, -7, -8].pack("s*") + shape = [2, 4] + strides = [4*sizeof(:short)*2, sizeof(:short)*2] + mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides) + assert_equal([1, 2], mv[[0, 0]]) + assert_equal([5, 6], mv[[0, 2]]) + assert_equal([-1, -2], mv[[1, 0]]) + assert_equal([-7, -8], mv[[1, 3]]) + end + + def test_ractor + assert_in_out_err([], <<-"end;", ["[5, 6]", "[-7, -8]"], []) + require "-test-/memory_view" + require "rbconfig/sizeof" + $VERBOSE = nil + r = Ractor.new RbConfig::SIZEOF["short"] do |sizeof_short| + buf = [ 1, 2, 3, 4, 5, 6, 7, 8, + -1, -2, -3, -4, -5, -6, -7, -8].pack("s*") + shape = [2, 4] + strides = [4*sizeof_short*2, sizeof_short*2] + mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides) + p mv[[0, 2]] + mv[[1, 3]] + end + p r.value + end; + end +end |
