1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_io.rb
nobu 59383b1f9c io.c: io_set_read_length
* io.c (io_set_read_length): if the read length equals to the buffer
  string size then nothing to do.  or ensure the string modifiable
  before setting the length only when the former is shorter.  based on
  the patch in [ruby-core:47541] by Hiroshi Shirosaki.
  [ruby-core:46586] [Bug #6764]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36980 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-09-16 02:39:18 +00:00

2499 lines
58 KiB
Ruby

require 'test/unit'
require 'tmpdir'
require "fcntl"
require 'io/nonblock'
require 'socket'
require 'stringio'
require 'timeout'
require 'tempfile'
require 'weakref'
require_relative 'envutil'
class TestIO < Test::Unit::TestCase
def have_close_on_exec?
begin
$stdin.close_on_exec?
true
rescue NotImplementedError
false
end
end
def have_nonblock?
IO.method_defined?("nonblock=")
end
def pipe(wp, rp)
re, we = nil, nil
r, w = IO.pipe
rt = Thread.new do
begin
rp.call(r)
rescue Exception
r.close
re = $!
end
end
wt = Thread.new do
begin
wp.call(w)
rescue Exception
w.close
we = $!
end
end
flunk("timeout") unless wt.join(10) && rt.join(10)
ensure
w.close unless !w || w.closed?
r.close unless !r || r.closed?
(wt.kill; wt.join) if wt
(rt.kill; rt.join) if rt
raise we if we
raise re if re
end
def with_pipe
r, w = IO.pipe
begin
yield r, w
ensure
r.close unless r.closed?
w.close unless w.closed?
end
end
def with_read_pipe(content)
pipe(proc do |w|
w << content
w.close
end, proc do |r|
yield r
end)
end
def mkcdtmpdir
Dir.mktmpdir {|d|
Dir.chdir(d) {
yield
}
}
end
def trapping_usr1
@usr1_rcvd = 0
trap(:USR1) { @usr1_rcvd += 1 }
yield
ensure
trap(:USR1, "DEFAULT")
end
def test_pipe
r, w = IO.pipe
assert_instance_of(IO, r)
assert_instance_of(IO, w)
[
Thread.start{
w.print "abc"
w.close
},
Thread.start{
assert_equal("abc", r.read)
r.close
}
].each{|thr| thr.join}
end
def test_pipe_block
x = nil
ret = IO.pipe {|r, w|
x = [r,w]
assert_instance_of(IO, r)
assert_instance_of(IO, w)
[
Thread.start do
w.print "abc"
w.close
end,
Thread.start do
assert_equal("abc", r.read)
end
].each{|thr| thr.join}
assert(!r.closed?)
assert(w.closed?)
:foooo
}
assert_equal(:foooo, ret)
assert(x[0].closed?)
assert(x[1].closed?)
end
def test_pipe_block_close
4.times {|i|
x = nil
IO.pipe {|r, w|
x = [r,w]
r.close if (i&1) == 0
w.close if (i&2) == 0
}
assert(x[0].closed?)
assert(x[1].closed?)
}
end
def test_gets_rs
# default_rs
pipe(proc do |w|
w.print "aaa\nbbb\n"
w.close
end, proc do |r|
assert_equal "aaa\n", r.gets
assert_equal "bbb\n", r.gets
assert_nil r.gets
r.close
end)
# nil
pipe(proc do |w|
w.print "a\n\nb\n\n"
w.close
end, proc do |r|
assert_equal "a\n\nb\n\n", r.gets(nil)
assert_nil r.gets("")
r.close
end)
# "\377"
pipe(proc do |w|
w.print "\377xyz"
w.close
end, proc do |r|
r.binmode
assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]")
r.close
end)
# ""
pipe(proc do |w|
w.print "a\n\nb\n\n"
w.close
end, proc do |r|
assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]"
assert_equal "b\n\n", r.gets("")
assert_nil r.gets("")
r.close
end)
end
def test_gets_limit_extra_arg
pipe(proc do |w|
w << "0123456789\n0123456789"
w.close
end, proc do |r|
assert_equal("0123456789\n0", r.gets(nil, 12))
assert_raise(TypeError) { r.gets(3,nil) }
end)
end
# This test cause SEGV.
def test_ungetc
pipe(proc do |w|
w.close
end, proc do |r|
s = "a" * 1000
assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } }
end)
end
def test_ungetbyte
make_tempfile {|t|
t.open
t.binmode
t.ungetbyte(0x41)
assert_equal(-1, t.pos)
assert_equal(0x41, t.getbyte)
t.rewind
assert_equal(0, t.pos)
t.ungetbyte("qux")
assert_equal(-3, t.pos)
assert_equal("quxfoo\n", t.gets)
assert_equal(4, t.pos)
t.set_encoding("utf-8")
t.ungetbyte(0x89)
t.ungetbyte(0x8e)
t.ungetbyte("\xe7")
t.ungetbyte("\xe7\xb4\x85")
assert_equal(-2, t.pos)
assert_equal("\u7d05\u7389bar\n", t.gets)
}
end
def test_each_byte
pipe(proc do |w|
w << "abc def"
w.close
end, proc do |r|
r.each_byte {|byte| break if byte == 32 }
assert_equal("def", r.read, "[ruby-dev:31659]")
end)
end
def test_each_byte_with_seek
make_tempfile {|t|
bug5119 = '[ruby-core:38609]'
i = 0
open(t.path) do |f|
f.each_byte {i = f.pos}
end
assert_equal(12, i, bug5119)
}
end
def test_each_codepoint
make_tempfile {|t|
bug2959 = '[ruby-core:28650]'
a = ""
File.open(t, 'rt') {|f|
f.each_codepoint {|c| a << c}
}
assert_equal("foo\nbar\nbaz\n", a, bug2959)
}
end
def test_rubydev33072
t = make_tempfile
path = t.path
t.close!
assert_raise(Errno::ENOENT, "[ruby-dev:33072]") do
File.read(path, nil, nil, {})
end
end
def test_copy_stream
mkcdtmpdir {
content = "foobar"
File.open("src", "w") {|f| f << content }
ret = IO.copy_stream("src", "dst")
assert_equal(content.bytesize, ret)
assert_equal(content, File.read("dst"))
# overwrite by smaller file.
content = "baz"
File.open("src", "w") {|f| f << content }
ret = IO.copy_stream("src", "dst")
assert_equal(content.bytesize, ret)
assert_equal(content, File.read("dst"))
ret = IO.copy_stream("src", "dst", 2)
assert_equal(2, ret)
assert_equal(content[0,2], File.read("dst"))
ret = IO.copy_stream("src", "dst", 0)
assert_equal(0, ret)
assert_equal("", File.read("dst"))
ret = IO.copy_stream("src", "dst", nil, 1)
assert_equal(content.bytesize-1, ret)
assert_equal(content[1..-1], File.read("dst"))
assert_raise(Errno::ENOENT) {
IO.copy_stream("nodir/foo", "dst")
}
assert_raise(Errno::ENOENT) {
IO.copy_stream("src", "nodir/bar")
}
pipe(proc do |w|
ret = IO.copy_stream("src", w)
assert_equal(content.bytesize, ret)
w.close
end, proc do |r|
assert_equal(content, r.read)
end)
with_pipe {|r, w|
w.close
assert_raise(IOError) { IO.copy_stream("src", w) }
}
pipe_content = "abc"
with_read_pipe(pipe_content) {|r|
ret = IO.copy_stream(r, "dst")
assert_equal(pipe_content.bytesize, ret)
assert_equal(pipe_content, File.read("dst"))
}
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
w2.sync = false
w2 << "def"
ret = IO.copy_stream(r1, w2)
assert_equal(2, ret)
w2.close
end, proc do |r2|
assert_equal("defbc", r2.read)
end)
}
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
w2.sync = false
w2 << "def"
ret = IO.copy_stream(r1, w2, 1)
assert_equal(1, ret)
w2.close
end, proc do |r2|
assert_equal("defb", r2.read)
end)
}
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
ret = IO.copy_stream(r1, w2)
assert_equal(2, ret)
w2.close
end, proc do |r2|
assert_equal("bc", r2.read)
end)
}
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
ret = IO.copy_stream(r1, w2, 1)
assert_equal(1, ret)
w2.close
end, proc do |r2|
assert_equal("b", r2.read)
end)
}
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
ret = IO.copy_stream(r1, w2, 0)
assert_equal(0, ret)
w2.close
end, proc do |r2|
assert_equal("", r2.read)
end)
}
pipe(proc do |w1|
w1 << "abc"
w1 << "def"
w1.close
end, proc do |r1|
assert_equal("a", r1.getc)
pipe(proc do |w2|
ret = IO.copy_stream(r1, w2)
assert_equal(5, ret)
w2.close
end, proc do |r2|
assert_equal("bcdef", r2.read)
end)
end)
pipe(proc do |w|
ret = IO.copy_stream("src", w, 1, 1)
assert_equal(1, ret)
w.close
end, proc do |r|
assert_equal(content[1,1], r.read)
end)
if have_nonblock?
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
with_pipe {|r2, w2|
begin
w2.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
break
end
s = w2.syswrite("a" * 100000)
t = Thread.new { sleep 0.1; r2.read }
ret = IO.copy_stream(r1, w2)
w2.close
assert_equal(2, ret)
assert_equal("a" * s + "bc", t.value)
}
}
end
bigcontent = "abc" * 123456
File.open("bigsrc", "w") {|f| f << bigcontent }
ret = IO.copy_stream("bigsrc", "bigdst")
assert_equal(bigcontent.bytesize, ret)
assert_equal(bigcontent, File.read("bigdst"))
File.unlink("bigdst")
ret = IO.copy_stream("bigsrc", "bigdst", nil, 100)
assert_equal(bigcontent.bytesize-100, ret)
assert_equal(bigcontent[100..-1], File.read("bigdst"))
File.unlink("bigdst")
ret = IO.copy_stream("bigsrc", "bigdst", 30000, 100)
assert_equal(30000, ret)
assert_equal(bigcontent[100, 30000], File.read("bigdst"))
File.open("bigsrc") {|f|
begin
assert_equal(0, f.pos)
ret = IO.copy_stream(f, "bigdst", nil, 10)
assert_equal(bigcontent.bytesize-10, ret)
assert_equal(bigcontent[10..-1], File.read("bigdst"))
assert_equal(0, f.pos)
ret = IO.copy_stream(f, "bigdst", 40, 30)
assert_equal(40, ret)
assert_equal(bigcontent[30, 40], File.read("bigdst"))
assert_equal(0, f.pos)
rescue NotImplementedError
#skip "pread(2) is not implemtented."
end
}
with_pipe {|r, w|
w.close
assert_raise(IOError) { IO.copy_stream("src", w) }
}
megacontent = "abc" * 1234567
File.open("megasrc", "w") {|f| f << megacontent }
if have_nonblock?
with_pipe {|r1, w1|
with_pipe {|r2, w2|
begin
r1.nonblock = true
w2.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
end
t1 = Thread.new { w1 << megacontent; w1.close }
t2 = Thread.new { r2.read }
ret = IO.copy_stream(r1, w2)
assert_equal(megacontent.bytesize, ret)
w2.close
t1.join
assert_equal(megacontent, t2.value)
}
}
end
with_pipe {|r1, w1|
with_pipe {|r2, w2|
t1 = Thread.new { w1 << megacontent; w1.close }
t2 = Thread.new { r2.read }
ret = IO.copy_stream(r1, w2)
assert_equal(megacontent.bytesize, ret)
w2.close
t1.join
assert_equal(megacontent, t2.value)
}
}
with_pipe {|r, w|
t = Thread.new { r.read }
ret = IO.copy_stream("megasrc", w)
assert_equal(megacontent.bytesize, ret)
w.close
assert_equal(megacontent, t.value)
}
}
end
def test_copy_stream_rbuf
mkcdtmpdir {
begin
pipe(proc do |w|
File.open("foo", "w") {|f| f << "abcd" }
File.open("foo") {|f|
f.read(1)
assert_equal(3, IO.copy_stream(f, w, 10, 1))
}
w.close
end, proc do |r|
assert_equal("bcd", r.read)
end)
rescue NotImplementedError
skip "pread(2) is not implemtented."
end
}
end
def with_socketpair
s1, s2 = UNIXSocket.pair
begin
yield s1, s2
ensure
s1.close unless s1.closed?
s2.close unless s2.closed?
end
end
def test_copy_stream_socket1
mkcdtmpdir {
content = "foobar"
File.open("src", "w") {|f| f << content }
with_socketpair {|s1, s2|
ret = IO.copy_stream("src", s1)
assert_equal(content.bytesize, ret)
s1.close
assert_equal(content, s2.read)
}
}
end if defined? UNIXSocket
def test_copy_stream_socket2
mkcdtmpdir {
bigcontent = "abc" * 123456
File.open("bigsrc", "w") {|f| f << bigcontent }
with_socketpair {|s1, s2|
t = Thread.new { s2.read }
ret = IO.copy_stream("bigsrc", s1)
assert_equal(bigcontent.bytesize, ret)
s1.close
result = t.value
assert_equal(bigcontent, result)
}
}
end if defined? UNIXSocket
def test_copy_stream_socket3
mkcdtmpdir {
bigcontent = "abc" * 123456
File.open("bigsrc", "w") {|f| f << bigcontent }
with_socketpair {|s1, s2|
t = Thread.new { s2.read }
ret = IO.copy_stream("bigsrc", s1, 10000)
assert_equal(10000, ret)
s1.close
result = t.value
assert_equal(bigcontent[0,10000], result)
}
}
end if defined? UNIXSocket
def test_copy_stream_socket4
mkcdtmpdir {
bigcontent = "abc" * 123456
File.open("bigsrc", "w") {|f| f << bigcontent }
File.open("bigsrc") {|f|
assert_equal(0, f.pos)
with_socketpair {|s1, s2|
t = Thread.new { s2.read }
ret = IO.copy_stream(f, s1, nil, 100)
assert_equal(bigcontent.bytesize-100, ret)
assert_equal(0, f.pos)
s1.close
result = t.value
assert_equal(bigcontent[100..-1], result)
}
}
}
end if defined? UNIXSocket
def test_copy_stream_socket5
mkcdtmpdir {
bigcontent = "abc" * 123456
File.open("bigsrc", "w") {|f| f << bigcontent }
File.open("bigsrc") {|f|
assert_equal(bigcontent[0,100], f.read(100))
assert_equal(100, f.pos)
with_socketpair {|s1, s2|
t = Thread.new { s2.read }
ret = IO.copy_stream(f, s1)
assert_equal(bigcontent.bytesize-100, ret)
assert_equal(bigcontent.length, f.pos)
s1.close
result = t.value
assert_equal(bigcontent[100..-1], result)
}
}
}
end if defined? UNIXSocket
def test_copy_stream_socket6
mkcdtmpdir {
megacontent = "abc" * 1234567
File.open("megasrc", "w") {|f| f << megacontent }
with_socketpair {|s1, s2|
begin
s1.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
end
t = Thread.new { s2.read }
ret = IO.copy_stream("megasrc", s1)
assert_equal(megacontent.bytesize, ret)
s1.close
result = t.value
assert_equal(megacontent, result)
}
}
end if defined? UNIXSocket
def test_copy_stream_socket7
mkcdtmpdir {
megacontent = "abc" * 1234567
File.open("megasrc", "w") {|f| f << megacontent }
with_socketpair {|s1, s2|
begin
s1.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
end
trapping_usr1 do
nr = 30
begin
pid = fork do
s1.close
IO.select([s2])
Process.kill(:USR1, Process.ppid)
s2.read
end
s2.close
nr.times do
assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1)
end
assert_equal(1, @usr1_rcvd)
ensure
s1.close
_, status = Process.waitpid2(pid) if pid
end
assert status.success?, status.inspect
end
}
}
end if defined? UNIXSocket and IO.method_defined?("nonblock=")
def test_copy_stream_strio
src = StringIO.new("abcd")
dst = StringIO.new
ret = IO.copy_stream(src, dst)
assert_equal(4, ret)
assert_equal("abcd", dst.string)
assert_equal(4, src.pos)
end
def test_copy_stream_strio_len
src = StringIO.new("abcd")
dst = StringIO.new
ret = IO.copy_stream(src, dst, 3)
assert_equal(3, ret)
assert_equal("abc", dst.string)
assert_equal(3, src.pos)
end
def test_copy_stream_strio_off
src = StringIO.new("abcd")
with_pipe {|r, w|
assert_raise(ArgumentError) {
IO.copy_stream(src, w, 3, 1)
}
}
end
def test_copy_stream_fname_to_strio
mkcdtmpdir {
File.open("foo", "w") {|f| f << "abcd" }
src = "foo"
dst = StringIO.new
ret = IO.copy_stream(src, dst, 3)
assert_equal(3, ret)
assert_equal("abc", dst.string)
}
end
def test_copy_stream_strio_to_fname
mkcdtmpdir {
# StringIO to filename
src = StringIO.new("abcd")
ret = IO.copy_stream(src, "fooo", 3)
assert_equal(3, ret)
assert_equal("abc", File.read("fooo"))
assert_equal(3, src.pos)
}
end
def test_copy_stream_io_to_strio
mkcdtmpdir {
# IO to StringIO
File.open("bar", "w") {|f| f << "abcd" }
File.open("bar") {|src|
dst = StringIO.new
ret = IO.copy_stream(src, dst, 3)
assert_equal(3, ret)
assert_equal("abc", dst.string)
assert_equal(3, src.pos)
}
}
end
def test_copy_stream_strio_to_io
mkcdtmpdir {
# StringIO to IO
src = StringIO.new("abcd")
ret = File.open("baz", "w") {|dst|
IO.copy_stream(src, dst, 3)
}
assert_equal(3, ret)
assert_equal("abc", File.read("baz"))
assert_equal(3, src.pos)
}
end
class Rot13IO
def initialize(io)
@io = io
end
def readpartial(*args)
ret = @io.readpartial(*args)
ret.tr!('a-zA-Z', 'n-za-mN-ZA-M')
ret
end
def write(str)
@io.write(str.tr('a-zA-Z', 'n-za-mN-ZA-M'))
end
def to_io
@io
end
end
def test_copy_stream_io_to_rot13
mkcdtmpdir {
File.open("bar", "w") {|f| f << "vex" }
File.open("bar") {|src|
File.open("baz", "w") {|dst0|
dst = Rot13IO.new(dst0)
ret = IO.copy_stream(src, dst, 3)
assert_equal(3, ret)
}
assert_equal("irk", File.read("baz"))
}
}
end
def test_copy_stream_rot13_to_io
mkcdtmpdir {
File.open("bar", "w") {|f| f << "flap" }
File.open("bar") {|src0|
src = Rot13IO.new(src0)
File.open("baz", "w") {|dst|
ret = IO.copy_stream(src, dst, 4)
assert_equal(4, ret)
}
}
assert_equal("sync", File.read("baz"))
}
end
def test_copy_stream_rot13_to_rot13
mkcdtmpdir {
File.open("bar", "w") {|f| f << "bin" }
File.open("bar") {|src0|
src = Rot13IO.new(src0)
File.open("baz", "w") {|dst0|
dst = Rot13IO.new(dst0)
ret = IO.copy_stream(src, dst, 3)
assert_equal(3, ret)
}
}
assert_equal("bin", File.read("baz"))
}
end
def test_copy_stream_strio_flush
with_pipe {|r, w|
w.sync = false
w.write "zz"
src = StringIO.new("abcd")
IO.copy_stream(src, w)
t = Thread.new {
w.close
}
assert_equal("zzabcd", r.read)
t.join
}
end
def test_copy_stream_strio_rbuf
pipe(proc do |w|
w << "abcd"
w.close
end, proc do |r|
assert_equal("a", r.read(1))
sio = StringIO.new
IO.copy_stream(r, sio)
assert_equal("bcd", sio.string)
end)
end
def test_copy_stream_src_wbuf
mkcdtmpdir {
pipe(proc do |w|
File.open("foe", "w+") {|f|
f.write "abcd\n"
f.rewind
f.write "xy"
IO.copy_stream(f, w)
}
assert_equal("xycd\n", File.read("foe"))
w.close
end, proc do |r|
assert_equal("cd\n", r.read)
r.close
end)
}
end
class Bug5237
attr_reader :count
def initialize
@count = 0
end
def read(bytes, buffer)
@count += 1
buffer.replace "this is a test"
nil
end
end
def test_copy_stream_broken_src_read_eof
src = Bug5237.new
dst = StringIO.new
assert_equal 0, src.count
th = Thread.new { IO.copy_stream(src, dst) }
flunk("timeout") unless th.join(10)
assert_equal 1, src.count
end
def test_copy_stream_dst_rbuf
mkcdtmpdir {
pipe(proc do |w|
w << "xyz"
w.close
end, proc do |r|
File.open("fom", "w+b") {|f|
f.write "abcd\n"
f.rewind
assert_equal("abc", f.read(3))
f.ungetc "c"
IO.copy_stream(r, f)
}
assert_equal("abxyz", File.read("fom"))
end)
}
end
def safe_4
t = Thread.new do
$SAFE = 4
yield
end
unless t.join(10)
t.kill
flunk("timeout in safe_4")
end
end
def ruby(*args)
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
f = IO.popen([ruby] + args, 'r+')
yield(f)
ensure
f.close unless !f || f.closed?
end
def test_try_convert
assert_equal(STDOUT, IO.try_convert(STDOUT))
assert_equal(nil, IO.try_convert("STDOUT"))
end
def test_ungetc2
f = false
pipe(proc do |w|
Thread.pass until f
w.write("1" * 10000)
w.close
end, proc do |r|
r.ungetc("0" * 10000)
f = true
assert_equal("0" * 10000 + "1" * 10000, r.read)
end)
end
def test_write_non_writable
with_pipe do |r, w|
assert_raise(IOError) do
r.write "foobarbaz"
end
end
end
def test_dup
ruby do |f|
f2 = f.dup
f.puts "foo"
f2.puts "bar"
f.close_write
f2.close_write
assert_equal("foo\nbar\n", f.read)
assert_equal("", f2.read)
end
end
def test_dup_many
ruby('-e', <<-'End') {|f|
ok = 0
a = []
begin
loop {a << IO.pipe}
rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
ok += 1
end
print "no" if ok != 1
begin
loop {a << [a[-1][0].dup, a[-1][1].dup]}
rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
ok += 1
end
print "no" if ok != 2
print "ok"
End
assert_equal("ok", f.read)
}
end
def test_inspect
with_pipe do |r, w|
assert_match(/^#<IO:fd \d+>$/, r.inspect)
assert_raise(SecurityError) do
safe_4 { r.inspect }
end
end
end
def test_readpartial
pipe(proc do |w|
w.write "foobarbaz"
w.close
end, proc do |r|
assert_raise(ArgumentError) { r.readpartial(-1) }
assert_equal("fooba", r.readpartial(5))
r.readpartial(5, s = "")
assert_equal("rbaz", s)
end)
end
def test_readpartial_lock
with_pipe do |r, w|
s = ""
t = Thread.new { r.readpartial(5, s) }
Thread.pass until t.stop?
assert_raise(RuntimeError) { s.clear }
w.write "foobarbaz"
w.close
assert_equal("fooba", t.value)
end
end
def test_readpartial_pos
mkcdtmpdir {
open("foo", "w") {|f| f << "abc" }
open("foo") {|f|
f.seek(0)
assert_equal("ab", f.readpartial(2))
assert_equal(2, f.pos)
}
}
end
def test_readpartial_with_not_empty_buffer
pipe(proc do |w|
w.write "foob"
w.close
end, proc do |r|
r.readpartial(5, s = "01234567")
assert_equal("foob", s)
end)
end
def test_readpartial_buffer_error
with_pipe do |r, w|
s = ""
t = Thread.new { r.readpartial(5, s) }
Thread.pass until t.stop?
t.kill
t.value
assert_equal("", s)
end
end
def test_read
pipe(proc do |w|
w.write "foobarbaz"
w.close
end, proc do |r|
assert_raise(ArgumentError) { r.read(-1) }
assert_equal("fooba", r.read(5))
r.read(nil, s = "")
assert_equal("rbaz", s)
end)
end
def test_read_lock
with_pipe do |r, w|
s = ""
t = Thread.new { r.read(5, s) }
Thread.pass until t.stop?
assert_raise(RuntimeError) { s.clear }
w.write "foobarbaz"
w.close
assert_equal("fooba", t.value)
end
end
def test_read_with_not_empty_buffer
pipe(proc do |w|
w.write "foob"
w.close
end, proc do |r|
r.read(nil, s = "01234567")
assert_equal("foob", s)
end)
end
def test_read_buffer_error
with_pipe do |r, w|
s = ""
t = Thread.new { r.read(5, s) }
Thread.pass until t.stop?
t.kill
t.value
assert_equal("", s)
end
end
def test_write_nonblock
skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
pipe(proc do |w|
w.write_nonblock(1)
w.close
end, proc do |r|
assert_equal("1", r.read)
end)
end
def test_read_nonblock_with_not_empty_buffer
skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
w.write "foob"
w.close
r.read_nonblock(5, s = "01234567")
assert_equal("foob", s)
}
end
def test_read_nonblock_error
return if !have_nonblock?
skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
begin
r.read_nonblock 4096
rescue Errno::EWOULDBLOCK
assert_kind_of(IO::WaitReadable, $!)
end
}
end
def test_write_nonblock_error
return if !have_nonblock?
skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
begin
loop {
w.write_nonblock "a"*100000
}
rescue Errno::EWOULDBLOCK
assert_kind_of(IO::WaitWritable, $!)
end
}
end
def test_gets
pipe(proc do |w|
w.write "foobarbaz"
w.close
end, proc do |r|
assert_equal("", r.gets(0))
assert_equal("foobarbaz", r.gets(9))
end)
end
def test_close_read
ruby do |f|
f.close_read
f.write "foobarbaz"
assert_raise(IOError) { f.read }
end
end
def test_close_read_pipe
with_pipe do |r, w|
r.close_read
assert_raise(Errno::EPIPE) { w.write "foobarbaz" }
end
end
def test_close_read_security_error
with_pipe do |r, w|
assert_raise(SecurityError) do
safe_4 { r.close_read }
end
end
end
def test_close_read_non_readable
with_pipe do |r, w|
assert_raise(IOError) do
w.close_read
end
end
end
def test_close_write
ruby do |f|
f.write "foobarbaz"
f.close_write
assert_equal("foobarbaz", f.read)
end
end
def test_close_write_security_error
with_pipe do |r, w|
assert_raise(SecurityError) do
safe_4 { r.close_write }
end
end
end
def test_close_write_non_readable
with_pipe do |r, w|
assert_raise(IOError) do
r.close_write
end
end
end
def test_pid
r, w = IO.pipe
assert_equal(nil, r.pid)
assert_equal(nil, w.pid)
pipe = IO.popen(EnvUtil.rubybin, "r+")
pid1 = pipe.pid
pipe.puts "p $$"
pipe.close_write
pid2 = pipe.read.chomp.to_i
assert_equal(pid2, pid1)
assert_equal(pid2, pipe.pid)
pipe.close
assert_raise(IOError) { pipe.pid }
end
def make_tempfile
t = Tempfile.new("test_io")
t.binmode
t.puts "foo"
t.puts "bar"
t.puts "baz"
t.close
if block_given?
begin
yield t
ensure
t.close(true)
end
else
t
end
end
def test_set_lineno
make_tempfile {|t|
ruby("-e", <<-SRC, t.path) do |f|
open(ARGV[0]) do |f|
p $.
f.gets; p $.
f.gets; p $.
f.lineno = 1000; p $.
f.gets; p $.
f.gets; p $.
f.rewind; p $.
f.gets; p $.
f.gets; p $.
f.gets; p $.
f.gets; p $.
end
SRC
assert_equal("0,1,2,2,1001,1001,1001,1,2,3,3", f.read.chomp.gsub("\n", ","))
end
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
r.gets; assert_equal(1, $.)
r.gets; assert_equal(2, $.)
r.lineno = 1000; assert_equal(2, $.)
r.gets; assert_equal(1001, $.)
r.gets; assert_equal(1001, $.)
end)
}
end
def test_readline
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
r.readline; assert_equal(1, $.)
r.readline; assert_equal(2, $.)
r.lineno = 1000; assert_equal(2, $.)
r.readline; assert_equal(1001, $.)
assert_raise(EOFError) { r.readline }
end)
end
def test_each_char
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
a = []
r.each_char {|c| a << c }
assert_equal(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"], a)
end)
end
def test_lines
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
e = r.lines
assert_equal("foo\n", e.next)
assert_equal("bar\n", e.next)
assert_equal("baz\n", e.next)
assert_raise(StopIteration) { e.next }
end)
end
def test_bytes
pipe(proc do |w|
w.binmode
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
e = r.bytes
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c.ord, e.next)
end
assert_raise(StopIteration) { e.next }
end)
end
def test_chars
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
e = r.chars
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c, e.next)
end
assert_raise(StopIteration) { e.next }
end)
end
def test_readbyte
pipe(proc do |w|
w.binmode
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
r.binmode
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c.ord, r.readbyte)
end
assert_raise(EOFError) { r.readbyte }
end)
end
def test_readchar
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c, r.readchar)
end
assert_raise(EOFError) { r.readchar }
end)
end
def test_close_on_exec
skip "IO\#close_on_exec is not implemented." unless have_close_on_exec?
ruby do |f|
assert_equal(true, f.close_on_exec?)
f.close_on_exec = false
assert_equal(false, f.close_on_exec?)
f.close_on_exec = true
assert_equal(true, f.close_on_exec?)
f.close_on_exec = false
assert_equal(false, f.close_on_exec?)
end
with_pipe do |r, w|
assert_equal(true, r.close_on_exec?)
r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
r.close_on_exec = true
assert_equal(true, r.close_on_exec?)
r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
assert_equal(true, w.close_on_exec?)
w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
w.close_on_exec = true
assert_equal(true, w.close_on_exec?)
w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
end
end
def test_close_security_error
with_pipe do |r, w|
assert_raise(SecurityError) do
safe_4 { r.close }
end
end
end
def test_pos
make_tempfile {|t|
open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
f.write "Hello"
assert_equal(5, f.pos)
end
open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
f.sync = true
f.read
f.write "Hello"
assert_equal(5, f.pos)
end
}
end
def test_pos_with_getc
bug6179 = '[ruby-core:43497]'
make_tempfile {|t|
["", "t", "b"].each do |mode|
open(t.path, "w#{mode}") do |f|
f.write "0123456789\n"
end
open(t.path, "r#{mode}") do |f|
assert_equal 0, f.pos, "mode=r#{mode}"
assert_equal '0', f.getc, "mode=r#{mode}"
assert_equal 1, f.pos, "mode=r#{mode}"
assert_equal '1', f.getc, "mode=r#{mode}"
assert_equal 2, f.pos, "mode=r#{mode}"
assert_equal '2', f.getc, "mode=r#{mode}"
assert_equal 3, f.pos, "mode=r#{mode}"
assert_equal '3', f.getc, "mode=r#{mode}"
assert_equal 4, f.pos, "mode=r#{mode}"
assert_equal '4', f.getc, "mode=r#{mode}"
end
end
}
end
def test_seek
make_tempfile {|t|
open(t.path) { |f|
f.seek(9)
assert_equal("az\n", f.read)
}
open(t.path) { |f|
f.seek(9, IO::SEEK_SET)
assert_equal("az\n", f.read)
}
open(t.path) { |f|
f.seek(-4, IO::SEEK_END)
assert_equal("baz\n", f.read)
}
open(t.path) { |f|
assert_equal("foo\n", f.gets)
f.seek(2, IO::SEEK_CUR)
assert_equal("r\nbaz\n", f.read)
}
}
end
def test_sysseek
make_tempfile {|t|
open(t.path) do |f|
f.sysseek(-4, IO::SEEK_END)
assert_equal("baz\n", f.read)
end
open(t.path) do |f|
a = [f.getc, f.getc, f.getc]
a.reverse_each {|c| f.ungetc c }
assert_raise(IOError) { f.sysseek(1) }
end
}
end
def test_syswrite
make_tempfile {|t|
open(t.path, "w") do |f|
o = Object.new
def o.to_s; "FOO\n"; end
f.syswrite(o)
end
assert_equal("FOO\n", File.read(t.path))
}
end
def test_sysread
make_tempfile {|t|
open(t.path) do |f|
a = [f.getc, f.getc, f.getc]
a.reverse_each {|c| f.ungetc c }
assert_raise(IOError) { f.sysread(1) }
end
}
end
def test_sysread_with_not_empty_buffer
pipe(proc do |w|
w.write "foob"
w.close
end, proc do |r|
r.sysread( 5, s = "01234567" )
assert_equal( "foob", s )
end)
end
def test_flag
make_tempfile {|t|
assert_raise(ArgumentError) do
open(t.path, "z") { }
end
assert_raise(ArgumentError) do
open(t.path, "rr") { }
end
}
end
def test_sysopen
make_tempfile {|t|
fd = IO.sysopen(t.path)
assert_kind_of(Integer, fd)
f = IO.for_fd(fd)
assert_equal("foo\nbar\nbaz\n", f.read)
f.close
fd = IO.sysopen(t.path, "w", 0666)
assert_kind_of(Integer, fd)
if defined?(Fcntl::F_GETFL)
f = IO.for_fd(fd)
else
f = IO.for_fd(fd, 0666)
end
f.write("FOO\n")
f.close
fd = IO.sysopen(t.path, "r")
assert_kind_of(Integer, fd)
f = IO.for_fd(fd)
assert_equal("FOO\n", f.read)
f.close
}
end
def try_fdopen(fd, autoclose = true, level = 50)
if level > 0
begin
1.times {return try_fdopen(fd, autoclose, level - 1)}
ensure
GC.start
end
else
WeakRef.new(IO.for_fd(fd, autoclose: autoclose))
end
end
def test_autoclose
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
Dir.mktmpdir {|d|
t = open("#{d}/#{pre}", "w")
f = IO.for_fd(t.fileno)
assert_equal(true, f.autoclose?)
f.autoclose = false
assert_equal(false, f.autoclose?)
f.close
assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
t = open("#{d}/#{pre}", "w")
f = IO.for_fd(t.fileno, autoclose: false)
assert_equal(false, f.autoclose?)
f.autoclose = true
assert_equal(true, f.autoclose?)
f.close
assert_raise(Errno::EBADF, feature2250) {t.close}
}
end
def test_autoclose_true_closed_by_finalizer
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
t = Tempfile.new(pre)
w = try_fdopen(t.fileno)
begin
w.close
begin
t.close
rescue Errno::EBADF
end
skip "expect IO object was GC'ed but not recycled yet"
rescue WeakRef::RefError
assert_raise(Errno::EBADF, feature2250) {t.close}
end
ensure
t.unlink
end
def test_autoclose_false_closed_by_finalizer
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
t = Tempfile.new(pre)
w = try_fdopen(t.fileno, false)
begin
w.close
t.close
skip "expect IO object was GC'ed but not recycled yet"
rescue WeakRef::RefError
assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
end
ensure
t.unlink
end
def test_open_redirect
o = Object.new
def o.to_open; self; end
assert_equal(o, open(o))
o2 = nil
open(o) do |f|
o2 = f
end
assert_equal(o, o2)
end
def test_open_pipe
open("|" + EnvUtil.rubybin, "r+") do |f|
f.puts "puts 'foo'"
f.close_write
assert_equal("foo\n", f.read)
end
end
def test_reopen
make_tempfile {|t|
with_pipe do |r, w|
assert_raise(SecurityError) do
safe_4 { r.reopen(t.path) }
end
end
open(__FILE__) do |f|
f.gets
assert_nothing_raised {
f.reopen(t.path)
assert_equal("foo\n", f.gets)
}
end
open(__FILE__) do |f|
f.gets
f2 = open(t.path)
begin
f2.gets
assert_nothing_raised {
f.reopen(f2)
assert_equal("bar\n", f.gets, '[ruby-core:24240]')
}
ensure
f2.close
end
end
open(__FILE__) do |f|
f2 = open(t.path)
begin
f.reopen(f2)
assert_equal("foo\n", f.gets)
assert_equal("bar\n", f.gets)
f.reopen(f2)
assert_equal("baz\n", f.gets, '[ruby-dev:39479]')
ensure
f2.close
end
end
}
end
def test_reopen_inherit
mkcdtmpdir {
system(EnvUtil.rubybin, '-e', <<"End")
f = open("out", "w")
STDOUT.reopen(f)
STDERR.reopen(f)
system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"')
system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"')
End
assert_equal("outerr", File.read("out"))
}
end
def test_foreach
a = []
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
make_tempfile {|t|
a = []
IO.foreach(t.path) {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
IO.foreach(t.path, {:mode => "r" }) {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
IO.foreach(t.path, {:open_args => [] }) {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
IO.foreach(t.path, "b") {|x| a << x }
assert_equal(["foo\nb", "ar\nb", "az\n"], a)
a = []
IO.foreach(t.path, 3) {|x| a << x }
assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a)
a = []
IO.foreach(t.path, "b", 3) {|x| a << x }
assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a)
bug = '[ruby-dev:31525]'
assert_raise(ArgumentError, bug) {IO.foreach}
a = nil
assert_nothing_raised(ArgumentError, bug) {a = IO.foreach(t.path).to_a}
assert_equal(["foo\n", "bar\n", "baz\n"], a, bug)
bug6054 = '[ruby-dev:45267]'
e = assert_raise(IOError, bug6054) {IO.foreach(t.path, mode:"w").next}
assert_match(/not opened for reading/, e.message, bug6054)
}
end
def test_s_readlines
make_tempfile {|t|
assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b"))
assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2))
assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2))
}
end
def test_printf
pipe(proc do |w|
printf(w, "foo %s baz\n", "bar")
w.close_write
end, proc do |r|
assert_equal("foo bar baz\n", r.read)
end)
end
def test_print
make_tempfile {|t|
assert_in_out_err(["-", t.path],
"print while $<.gets",
%w(foo bar baz), [])
}
end
def test_print_separators
$, = ':'
$\ = "\n"
pipe(proc do |w|
w.print('a')
w.print('a','b','c')
w.close
end, proc do |r|
assert_equal("a\n", r.gets)
assert_equal("a:b:c\n", r.gets)
assert_nil r.gets
r.close
end)
ensure
$, = nil
$\ = nil
end
def test_putc
pipe(proc do |w|
w.putc "A"
w.putc "BC"
w.putc 68
w.close_write
end, proc do |r|
assert_equal("ABD", r.read)
end)
assert_in_out_err([], "putc 65", %w(A), [])
end
def test_puts_recursive_array
a = ["foo"]
a << a
pipe(proc do |w|
w.puts a
w.close
end, proc do |r|
assert_equal("foo\n[...]\n", r.read)
end)
end
def test_display
pipe(proc do |w|
"foo".display(w)
w.close
end, proc do |r|
assert_equal("foo", r.read)
end)
assert_in_out_err([], "'foo'.display", %w(foo), [])
end
def test_set_stdout
assert_raise(TypeError) { $> = Object.new }
assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
end
def test_initialize
return unless defined?(Fcntl::F_GETFL)
make_tempfile {|t|
fd = IO.sysopen(t.path, "w")
assert_kind_of(Integer, fd)
%w[r r+ w+ a+].each do |mode|
assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
end
f = IO.new(fd, "w")
f.write("FOO\n")
f.close
assert_equal("FOO\n", File.read(t.path))
}
end
def test_reinitialize
make_tempfile {|t|
f = open(t.path)
begin
assert_raise(RuntimeError) do
f.instance_eval { initialize }
end
ensure
f.close
end
}
end
def test_new_with_block
assert_in_out_err([], "r, w = IO.pipe; IO.new(r) {}", [], /^.+$/)
end
def test_readline2
assert_in_out_err(["-e", <<-SRC], "foo\nbar\nbaz\n", %w(foo bar baz end), [])
puts readline
puts readline
puts readline
begin
puts readline
rescue EOFError
puts "end"
end
SRC
end
def test_readlines
assert_in_out_err(["-e", "p readlines"], "foo\nbar\nbaz\n",
["[\"foo\\n\", \"bar\\n\", \"baz\\n\"]"], [])
end
def test_s_read
make_tempfile {|t|
assert_equal("foo\nbar\nbaz\n", File.read(t.path))
assert_equal("foo\nba", File.read(t.path, 6))
assert_equal("bar\n", File.read(t.path, 4, 4))
}
end
def test_uninitialized
assert_raise(IOError) { IO.allocate.print "" }
end
def test_nofollow
# O_NOFOLLOW is not standard.
return if /freebsd|linux/ !~ RUBY_PLATFORM
return unless defined? File::NOFOLLOW
mkcdtmpdir {
open("file", "w") {|f| f << "content" }
begin
File.symlink("file", "slnk")
rescue NotImplementedError
return
end
assert_raise(Errno::EMLINK, Errno::ELOOP) {
open("slnk", File::RDONLY|File::NOFOLLOW) {}
}
assert_raise(Errno::EMLINK, Errno::ELOOP) {
File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {}
}
}
end
def test_tainted
make_tempfile {|t|
assert(File.read(t.path, 4).tainted?, '[ruby-dev:38826]')
assert(File.open(t.path) {|f| f.read(4)}.tainted?, '[ruby-dev:38826]')
}
end
def test_binmode_after_closed
make_tempfile {|t|
assert_raise(IOError) {t.binmode}
}
end
def test_threaded_flush
bug3585 = '[ruby-core:31348]'
src = %q{\
t = Thread.new { sleep 3 }
Thread.new {sleep 1; t.kill; p 'hi!'}
t.join
}.gsub(/^\s+/, '')
10.times.map do
Thread.start do
assert_in_out_err([], src) {|stdout, stderr|
assert_no_match(/hi.*hi/, stderr.join, bug3585)
}
end
end.each {|th| th.join}
end
def test_flush_in_finalizer1
require 'tempfile'
bug3910 = '[ruby-dev:42341]'
Tempfile.open("bug3910") {|t|
path = t.path
t.close
fds = []
assert_nothing_raised(TypeError, bug3910) do
500.times {
f = File.open(path, "w")
fds << f.fileno
f.print "hoge"
}
end
t.unlink
}
ensure
GC.start
end
def test_flush_in_finalizer2
require 'tempfile'
bug3910 = '[ruby-dev:42341]'
Tempfile.open("bug3910") {|t|
path = t.path
t.close
1.times do
io = open(path,"w")
io.print "hoge"
end
assert_nothing_raised(TypeError, bug3910) do
GC.start
end
t.unlink
}
end
def test_readlines_limit_0
bug4024 = '[ruby-dev:42538]'
make_tempfile {|t|
open(t.path, "r") do |io|
assert_raise(ArgumentError, bug4024) do
io.readlines(0)
end
end
}
end
def test_each_line_limit_0
bug4024 = '[ruby-dev:42538]'
make_tempfile {|t|
open(t.path, "r") do |io|
assert_raise(ArgumentError, bug4024) do
io.each_line(0).next
end
end
}
end
def test_advise
make_tempfile {|tf|
assert_raise(ArgumentError, "no arguments") { tf.advise }
%w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv|
[[0,0], [0, 20], [400, 2]].each do |offset, len|
open(tf.path) do |t|
assert_equal(t.advise(adv, offset, len), nil)
assert_raise(ArgumentError, "superfluous arguments") do
t.advise(adv, offset, len, offset)
end
assert_raise(TypeError, "wrong type for first argument") do
t.advise(adv.to_s, offset, len)
end
assert_raise(TypeError, "wrong type for last argument") do
t.advise(adv, offset, Array(len))
end
assert_raise(RangeError, "last argument too big") do
t.advise(adv, offset, 9999e99)
end
end
assert_raise(IOError, "closed file") do
make_tempfile {|tf2|
tf2.advise(adv.to_sym, offset, len)
}
end
end
end
}
end
def test_invalid_advise
feature4204 = '[ruby-dev:42887]'
make_tempfile {|tf|
%w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv|
[[0,0], [0, 20], [400, 2]].each do |offset, len|
open(tf.path) do |t|
assert_raise(NotImplementedError, feature4204) { t.advise(adv, offset, len) }
end
end
end
}
end
def test_fcntl_lock_linux
return if /x86_64-linux/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
pad=0
Tempfile.open(self.class.name) do |f|
r, w = IO.pipe
pid = fork do
r.close
lock = [Fcntl::F_WRLCK, IO::SEEK_SET, pad, 12, 34, 0].pack("s!s!i!L!L!i!")
f.fcntl Fcntl::F_SETLKW, lock
w.syswrite "."
sleep
end
w.close
assert_equal ".", r.read(1)
r.close
pad = 0
getlock = [Fcntl::F_WRLCK, 0, pad, 0, 0, 0].pack("s!s!i!L!L!i!")
f.fcntl Fcntl::F_GETLK, getlock
ptype, whence, pad, start, len, lockpid = getlock.unpack("s!s!i!L!L!i!")
assert_equal(ptype, Fcntl::F_WRLCK)
assert_equal(whence, IO::SEEK_SET)
assert_equal(start, 12)
assert_equal(len, 34)
assert_equal(pid, lockpid)
Process.kill :TERM, pid
Process.waitpid2(pid)
f.close(true)
end
end
def test_fcntl_lock_freebsd
return if /freebsd/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
start = 12
len = 34
sysid = 0
Tempfile.open(self.class.name) do |f|
r, w = IO.pipe
pid = fork do
r.close
lock = [start, len, 0, Fcntl::F_WRLCK, IO::SEEK_SET, sysid].pack("qqis!s!i!")
f.fcntl Fcntl::F_SETLKW, lock
w.syswrite "."
sleep
end
w.close
assert_equal ".", r.read(1)
r.close
getlock = [0, 0, 0, Fcntl::F_WRLCK, 0, 0].pack("qqis!s!i!")
f.fcntl Fcntl::F_GETLK, getlock
start, len, lockpid, ptype, whence, sysid = getlock.unpack("qqis!s!i!")
assert_equal(ptype, Fcntl::F_WRLCK)
assert_equal(whence, IO::SEEK_SET)
assert_equal(start, 12)
assert_equal(len, 34)
assert_equal(pid, lockpid)
Process.kill :TERM, pid
Process.waitpid2(pid)
end
end
def test_fcntl_dupfd
Tempfile.open(self.class.name) do |f|
fd = f.fcntl(Fcntl::F_DUPFD, 63)
begin
assert_operator(fd, :>=, 63)
ensure
IO.for_fd(fd).close
end
f.unlink
end
end
def test_cross_thread_close_fd
skip "cross thread close causes hung-up if pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe do |r,w|
read_thread = Thread.new do
begin
r.read(1)
rescue => e
e
end
end
sleep(0.1) until read_thread.stop?
r.close
read_thread.join
assert_kind_of(IOError, read_thread.value)
end
end
def test_cross_thread_close_stdio
with_pipe do |r,w|
pid = fork do
$stdin.reopen(r)
r.close
read_thread = Thread.new do
begin
$stdin.read(1)
rescue => e
e
end
end
sleep(0.1) until read_thread.stop?
$stdin.close
read_thread.join
exit(IOError === read_thread.value)
end
assert Process.waitpid2(pid)[1].success?
end
rescue NotImplementedError
end
def test_open_mode
feature4742 = "[ruby-core:36338]"
bug6055 = '[ruby-dev:45268]'
mkcdtmpdir do
assert_not_nil(f = File.open('symbolic', 'w'))
f.close
assert_not_nil(f = File.open('numeric', File::WRONLY|File::TRUNC|File::CREAT))
f.close
assert_not_nil(f = File.open('hash-symbolic', :mode => 'w'))
f.close
assert_not_nil(f = File.open('hash-numeric', :mode => File::WRONLY|File::TRUNC|File::CREAT), feature4742)
f.close
assert_nothing_raised(bug6055) {f = File.open('hash-symbolic', binmode: true)}
f.close
end
end
def test_s_write
mkcdtmpdir do
path = "test_s_write"
File.write(path, "foo\nbar\nbaz")
assert_equal("foo\nbar\nbaz", File.read(path))
File.write(path, "FOO", 0)
assert_equal("FOO\nbar\nbaz", File.read(path))
File.write(path, "BAR")
assert_equal("BAR", File.read(path))
File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP")
assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP"))
File.delete path
assert_equal(6, File.write(path, 'string', 2))
File.delete path
assert_raise(Errno::EINVAL) { File.write('nonexisting','string', -2) }
assert_equal(6, File.write(path, 'string'))
assert_equal(3, File.write(path, 'sub', 1))
assert_equal("ssubng", File.read(path))
File.delete path
assert_equal(3, File.write(path, "foo", encoding: "UTF-8"))
File.delete path
assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8"))
assert_equal("foo", File.read(path))
assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
assert_equal("ffo", File.read(path))
File.delete path
assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
assert_equal("\00f", File.read(path))
assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8"))
assert_equal("ff", File.read(path))
end
end
def test_s_binwrite
mkcdtmpdir do
path = "test_s_binwrite"
File.binwrite(path, "foo\nbar\nbaz")
assert_equal("foo\nbar\nbaz", File.read(path))
File.binwrite(path, "FOO", 0)
assert_equal("FOO\nbar\nbaz", File.read(path))
File.binwrite(path, "BAR")
assert_equal("BAR", File.read(path))
File.binwrite(path, "\u{3042}")
assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path))
File.delete path
assert_equal(6, File.binwrite(path, 'string', 2))
File.delete path
assert_equal(6, File.binwrite(path, 'string'))
assert_equal(3, File.binwrite(path, 'sub', 1))
assert_equal("ssubng", File.binread(path))
assert_equal(6, File.size(path))
assert_raise(Errno::EINVAL) { File.binwrite('nonexisting', 'string', -2) }
assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") }
end
end
def test_race_between_read
file = Tempfile.new("test")
path = file.path
file.close
write_file = File.open(path, "wt")
read_file = File.open(path, "rt")
threads = []
10.times do |i|
threads << Thread.new {write_file.print(i)}
threads << Thread.new {read_file.read}
end
threads.each {|t| t.join}
assert(true, "[ruby-core:37197]")
ensure
read_file.close
write_file.close
file.close!
end
def test_warn
stderr = EnvUtil.verbose_warning do
warn "warning"
end
assert_equal("warning\n", stderr)
stderr = EnvUtil.verbose_warning do
warn
end
assert_equal("", stderr)
stderr = EnvUtil.verbose_warning do
warn "[Feature #5029]", "[ruby-core:38070]"
end
assert_equal("[Feature #5029]\n[ruby-core:38070]\n", stderr)
end
def test_cloexec
return unless defined? Fcntl::FD_CLOEXEC
open(__FILE__) {|f|
assert(f.close_on_exec?)
g = f.dup
begin
assert(g.close_on_exec?)
f.reopen(g)
assert(f.close_on_exec?)
ensure
g.close
end
g = IO.new(f.fcntl(Fcntl::F_DUPFD))
begin
assert(g.close_on_exec?)
ensure
g.close
end
}
IO.pipe {|r,w|
assert(r.close_on_exec?)
assert(w.close_on_exec?)
}
end
def test_ioctl_linux
return if /linux/ !~ RUBY_PLATFORM
assert_nothing_raised do
File.open('/dev/urandom'){|f1|
entropy_count = ""
# get entropy count
f1.ioctl(0x80045200, entropy_count)
}
end
buf = ''
assert_nothing_raised do
fionread = 0x541B
File.open(__FILE__){|f1|
f1.ioctl(fionread, buf)
}
end
assert_equal(File.size(__FILE__), buf.unpack('i!')[0])
end
def test_ioctl_linux2
return if /linux/ !~ RUBY_PLATFORM
return if /^i?86|^x86_64/ !~ RUBY_PLATFORM
return if File.exist?('/dev/tty')
File.open('/dev/tty') { |f|
tiocgwinsz=0x5413
winsize=""
assert_nothing_raised {
f.ioctl(tiocgwinsz, winsize)
}
}
end
def test_setpos
mkcdtmpdir {
File.open("tmp.txt", "wb") {|f|
f.puts "a"
f.puts "bc"
f.puts "def"
}
pos1 = pos2 = pos3 = nil
File.open("tmp.txt", "rb") {|f|
assert_equal("a\n", f.gets)
pos1 = f.pos
assert_equal("bc\n", f.gets)
pos2 = f.pos
assert_equal("def\n", f.gets)
pos3 = f.pos
assert_equal(nil, f.gets)
}
File.open("tmp.txt", "rb") {|f|
f.pos = pos1
assert_equal("bc\n", f.gets)
assert_equal("def\n", f.gets)
assert_equal(nil, f.gets)
}
File.open("tmp.txt", "rb") {|f|
f.pos = pos2
assert_equal("def\n", f.gets)
assert_equal(nil, f.gets)
}
File.open("tmp.txt", "rb") {|f|
f.pos = pos3
assert_equal(nil, f.gets)
}
File.open("tmp.txt", "rb") {|f|
f.pos = File.size("tmp.txt")
s = "not empty string "
assert_equal("", f.read(0,s))
}
}
end
def test_std_fileno
assert_equal(0, STDIN.fileno)
assert_equal(1, STDOUT.fileno)
assert_equal(2, STDERR.fileno)
assert_equal(0, $stdin.fileno)
assert_equal(1, $stdout.fileno)
assert_equal(2, $stderr.fileno)
end
def test_sysread_locktmp
bug6099 = '[ruby-dev:45297]'
buf = " " * 100
data = "a" * 100
with_pipe do |r,w|
th = Thread.new {r.sysread(100, buf)}
Thread.pass until th.stop?
buf.replace("")
assert_empty(buf, bug6099)
w.write(data)
Thread.pass while th.alive?
th.join
end
assert_equal(data, buf, bug6099)
end
def test_readpartial_locktmp
skip "nonblocking mode is not supported for pipe on this platform" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
bug6099 = '[ruby-dev:45297]'
buf = " " * 100
data = "a" * 100
with_pipe do |r,w|
r.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
th = Thread.new {r.readpartial(100, buf)}
Thread.pass until th.stop?
buf.replace("")
assert_empty(buf, bug6099)
w.write(data)
Thread.pass while th.alive?
th.join
end
assert_equal(data, buf, bug6099)
rescue RuntimeError # can't modify string; temporarily locked
end
def test_advise_pipe
# we don't know if other platforms have a real posix_fadvise()
return if /linux/ !~ RUBY_PLATFORM
with_pipe do |r,w|
# Linux 2.6.15 and earlier returned EINVAL instead of ESPIPE
assert_raise(Errno::ESPIPE, Errno::EINVAL) { r.advise(:willneed) }
assert_raise(Errno::ESPIPE, Errno::EINVAL) { w.advise(:willneed) }
end
end
def assert_buffer_not_raise_shared_string_error
bug6764 = '[ruby-core:46586]'
size = 28
data = [*"a".."z", *"A".."Z"].shuffle.join("")
t = Tempfile.new("test_io")
t.write(data)
t.close
w = Tempfile.new("test_io")
assert_nothing_raised(RuntimeError, bug6764) do
File.open(t.path, "r") do |r|
buf = ''
while yield(r, size, buf)
w << buf
end
end
end
w.close
assert_equal(data, w.open.read, bug6764)
ensure
t.close!
w.close!
end
def test_read_buffer_not_raise_shared_string_error
assert_buffer_not_raise_shared_string_error do |r, size, buf|
r.read(size, buf)
end
end
def test_sysread_buffer_not_raise_shared_string_error
assert_buffer_not_raise_shared_string_error do |r, size, buf|
begin
r.sysread(size, buf)
rescue EOFError
nil
end
end
end
def test_readpartial_buffer_not_raise_shared_string_error
assert_buffer_not_raise_shared_string_error do |r, size, buf|
begin
r.readpartial(size, buf)
rescue EOFError
nil
end
end
end
end