зеркало из https://github.com/github/ruby.git
480 строки
16 KiB
Ruby
480 строки
16 KiB
Ruby
# 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]",
|
|
]
|
|
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)
|
|
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_struct_memory_access()
|
|
# check memory operations performed directly on struct
|
|
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]
|
|
end
|
|
end
|
|
|
|
def test_struct_ptr_array_subscript_multiarg()
|
|
# check memory operations performed on struct#to_ptr
|
|
Fiddle::Importer.struct([ 'int x' ]).malloc(Fiddle::RUBY_FREE) do |struct|
|
|
ptr = struct.to_ptr
|
|
|
|
struct.x = 0x02020202
|
|
assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
|
|
|
|
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
|
|
assert_equal 0x01010101, struct.x
|
|
end
|
|
end
|
|
|
|
def test_malloc()
|
|
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)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_sizeof()
|
|
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
|
|
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
|
|
LIBC::MyStruct.malloc(Fiddle::RUBY_FREE) do |my_struct|
|
|
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct))
|
|
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
|
|
type = $&
|
|
const_type_name = $1
|
|
size = Fiddle.const_get("SIZEOF_#{const_type_name}")
|
|
if const_type_name == "CONST_STRING"
|
|
name = "const_string"
|
|
type_name = "const char*"
|
|
else
|
|
name = $1.sub(/P\z/,"*").gsub(/_(?!T\z)/, " ").downcase
|
|
type_name = name
|
|
end
|
|
define_method("test_sizeof_#{name}") do
|
|
assert_equal(size, Fiddle::Importer.sizeof(type_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_array_assignment()
|
|
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
|
|
assert_equal 1024, instance.stages[0]
|
|
assert_equal 10, instance.stages[1]
|
|
assert_equal 100, instance.stages[2]
|
|
assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3),
|
|
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
|
|
assert_raise(IndexError) { instance.stages[-1] = 5 }
|
|
assert_raise(IndexError) { instance.stages[3] = 5 }
|
|
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()
|
|
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)
|
|
end
|
|
end
|
|
|
|
def test_gettimeofday()
|
|
if( defined?(LIBC.gettimeofday) )
|
|
LIBC::Timeval.malloc(Fiddle::RUBY_FREE) do |timeval|
|
|
LIBC::Timezone.malloc(Fiddle::RUBY_FREE) do |timezone|
|
|
LIBC.gettimeofday(timeval, timezone)
|
|
end
|
|
cur = Time.now()
|
|
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
|
|
end
|
|
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)
|