mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
132 lines
3.2 KiB
Ruby
132 lines
3.2 KiB
Ruby
|
# frozen_string_literal: false
|
||
|
|
||
|
class TestIOBuffer < Test::Unit::TestCase
|
||
|
def assert_negative(value)
|
||
|
assert(value < 0, "Expected #{value} to be negative!")
|
||
|
end
|
||
|
|
||
|
def assert_positive(value)
|
||
|
assert(value > 0, "Expected #{value} to be positive!")
|
||
|
end
|
||
|
|
||
|
def test_flags
|
||
|
assert_equal 0, IO::Buffer::EXTERNAL
|
||
|
assert_equal 1, IO::Buffer::INTERNAL
|
||
|
assert_equal 2, IO::Buffer::MAPPED
|
||
|
|
||
|
assert_equal 16, IO::Buffer::LOCKED
|
||
|
assert_equal 32, IO::Buffer::PRIVATE
|
||
|
|
||
|
assert_equal 64, IO::Buffer::IMMUTABLE
|
||
|
end
|
||
|
|
||
|
def test_endian
|
||
|
assert_equal 4, IO::Buffer::LITTLE_ENDIAN
|
||
|
assert_equal 8, IO::Buffer::BIG_ENDIAN
|
||
|
assert_equal 8, IO::Buffer::NETWORK_ENDIAN
|
||
|
|
||
|
assert_include [IO::Buffer::LITTLE_ENDIAN, IO::Buffer::BIG_ENDIAN], IO::Buffer::HOST_ENDIAN
|
||
|
end
|
||
|
|
||
|
def test_new_internal
|
||
|
buffer = IO::Buffer.new(1024, IO::Buffer::INTERNAL)
|
||
|
assert_equal 1024, buffer.size
|
||
|
refute buffer.external?
|
||
|
assert buffer.internal?
|
||
|
refute buffer.mapped?
|
||
|
end
|
||
|
|
||
|
def test_new_mapped
|
||
|
buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED)
|
||
|
assert_equal 1024, buffer.size
|
||
|
refute buffer.external?
|
||
|
refute buffer.internal?
|
||
|
assert buffer.mapped?
|
||
|
end
|
||
|
|
||
|
def test_file_mapped
|
||
|
buffer = File.open(__FILE__) {|file| IO::Buffer.map(file)}
|
||
|
assert_include buffer.to_str, "Hello World"
|
||
|
end
|
||
|
|
||
|
def test_resize
|
||
|
buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED)
|
||
|
buffer.resize(2048, 0)
|
||
|
assert_equal 2048, buffer.size
|
||
|
end
|
||
|
|
||
|
def test_resize_preserve
|
||
|
message = "Hello World"
|
||
|
buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED)
|
||
|
buffer.copy(message, 0)
|
||
|
buffer.resize(2048, 1024)
|
||
|
assert_equal message, buffer.to_str(0, message.bytesize)
|
||
|
end
|
||
|
|
||
|
def test_compare_same_size
|
||
|
buffer1 = IO::Buffer.new(1)
|
||
|
assert_equal buffer1, buffer1
|
||
|
|
||
|
buffer2 = IO::Buffer.new(1)
|
||
|
buffer1.set(:U8, 0, 0x10)
|
||
|
buffer2.set(:U8, 0, 0x20)
|
||
|
|
||
|
assert_negative buffer1 <=> buffer2
|
||
|
assert_positive buffer2 <=> buffer1
|
||
|
end
|
||
|
|
||
|
def test_compare_different_size
|
||
|
buffer1 = IO::Buffer.new(3)
|
||
|
buffer2 = IO::Buffer.new(5)
|
||
|
|
||
|
assert_negative buffer1 <=> buffer2
|
||
|
assert_positive buffer2 <=> buffer1
|
||
|
end
|
||
|
|
||
|
def test_slice
|
||
|
buffer = IO::Buffer.new(128)
|
||
|
slice = buffer.slice(8, 32)
|
||
|
slice.copy("Hello World", 0)
|
||
|
assert_equal("Hello World", buffer.to_str(8, 11))
|
||
|
end
|
||
|
|
||
|
def test_slice_bounds
|
||
|
buffer = IO::Buffer.new(128)
|
||
|
|
||
|
# What is best exception class?
|
||
|
assert_raise RuntimeError do
|
||
|
buffer.slice(128, 10)
|
||
|
end
|
||
|
|
||
|
# assert_raise RuntimeError do
|
||
|
# pp buffer.slice(-10, 10)
|
||
|
# end
|
||
|
end
|
||
|
|
||
|
def test_invalidation
|
||
|
input, output = IO.pipe
|
||
|
|
||
|
# (1) rb_write_internal creates IO::Buffer object,
|
||
|
buffer = IO::Buffer.new(128)
|
||
|
|
||
|
# (2) it is passed to (malicious) scheduler
|
||
|
# (3) scheduler starts a thread which call system call with the buffer object
|
||
|
thread = Thread.new{buffer.locked{input.read}}
|
||
|
|
||
|
Thread.pass until thread.stop?
|
||
|
|
||
|
# (4) scheduler returns
|
||
|
# (5) rb_write_internal invalidate the buffer object
|
||
|
assert_raise RuntimeError do
|
||
|
buffer.free
|
||
|
end
|
||
|
|
||
|
# (6) the system call access the memory area after invalidation
|
||
|
output.write("Hello World")
|
||
|
output.close
|
||
|
thread.join
|
||
|
|
||
|
input.close
|
||
|
end
|
||
|
end
|