summaryrefslogtreecommitdiff
path: root/test/ruby/test_io.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_io.rb')
-rw-r--r--test/ruby/test_io.rb269
1 files changed, 212 insertions, 57 deletions
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 1ca05ae362..a78527d40e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -350,6 +350,19 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_ungetc_with_seek
+ make_tempfile {|t|
+ t.open
+ t.write('0123456789')
+ t.rewind
+
+ t.ungetc('a')
+ t.seek(2, :SET)
+
+ assert_equal('2', t.getc)
+ }
+ end
+
def test_ungetbyte
make_tempfile {|t|
t.open
@@ -373,6 +386,19 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_ungetbyte_with_seek
+ make_tempfile {|t|
+ t.open
+ t.write('0123456789')
+ t.rewind
+
+ t.ungetbyte('a'.ord)
+ t.seek(2, :SET)
+
+ assert_equal('2'.ord, t.getbyte)
+ }
+ end
+
def test_each_byte
pipe(proc do |w|
w << "abc def"
@@ -441,6 +467,24 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_each_codepoint_with_ungetc
+ bug21562 = '[ruby-core:123176] [Bug #21562]'
+ with_read_pipe("") {|p|
+ p.binmode
+ p.ungetc("aa")
+ a = ""
+ p.each_codepoint { |c| a << c }
+ assert_equal("aa", a, bug21562)
+ }
+ with_read_pipe("") {|p|
+ p.set_encoding("ascii-8bit", universal_newline: true)
+ p.ungetc("aa")
+ a = ""
+ p.each_codepoint { |c| a << c }
+ assert_equal("aa", a, bug21562)
+ }
+ end
+
def test_rubydev33072
t = make_tempfile
path = t.path
@@ -655,7 +699,6 @@ class TestIO < Test::Unit::TestCase
if have_nonblock?
def test_copy_stream_no_busy_wait
- omit "RJIT has busy wait on GC. This sometimes fails with --jit." if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
omit "multiple threads already active" if Thread.list.size > 1
msg = 'r58534 [ruby-core:80969] [Backport #13533]'
@@ -1116,6 +1159,34 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_dup_buffer
+ bug21131 = '[ruby-core:120961] [Bug #21131]'
+ mkcdtmpdir do
+ dst_class = Class.new do
+ def initialize(&block)
+ @block = block
+ end
+
+ def write(data)
+ @block.call(data.dup)
+ data.bytesize
+ end
+ end
+
+ rng = Random.new(42)
+ body = Tempfile.new("ruby-bug", binmode: true)
+ body.write(rng.bytes(16_385))
+ body.rewind
+
+ payload = []
+ IO.copy_stream(body, dst_class.new{payload << it})
+ body.rewind
+ assert_equal(body.read, payload.join, bug21131)
+ ensure
+ body&.close
+ end
+ end
+
def test_copy_stream_write_in_binmode
bug8767 = '[ruby-core:56518] [Bug #8767]'
mkcdtmpdir {
@@ -1322,10 +1393,6 @@ class TestIO < Test::Unit::TestCase
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
opts = {}
- if defined?(Process::RLIMIT_NPROC)
- lim = Process.getrlimit(Process::RLIMIT_NPROC)[1]
- opts[:rlimit_nproc] = [lim, 2048].min
- end
f = IO.popen([ruby] + args, 'r+', opts)
pid = f.pid
yield(f)
@@ -1679,7 +1746,6 @@ class TestIO < Test::Unit::TestCase
end if have_nonblock?
def test_read_nonblock_no_exceptions
- omit '[ruby-core:90895] RJIT worker may leave fd open in a forked child' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # TODO: consider acquiring GVL from RJIT worker.
with_pipe {|r, w|
assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
w.puts "HI!"
@@ -2002,6 +2068,44 @@ class TestIO < Test::Unit::TestCase
end
end
+ def test_readline_incompatible_rs
+ first_line = File.open(__FILE__, &:gets).encode("utf-32le")
+ File.open(__FILE__, encoding: "utf-8:utf-32le") {|f|
+ assert_equal first_line, f.readline
+ assert_raise(ArgumentError) {f.readline("\0")}
+ }
+ end
+
+ def test_readline_limit_nonascii
+ mkcdtmpdir do
+ i = 0
+
+ File.open("text#{i+=1}", "w+:utf-8") do |f|
+ f.write("Test\nok\u{bf}ok\n")
+ f.rewind
+
+ assert_equal("Test\nok\u{bf}", f.readline("\u{bf}"))
+ assert_equal("ok\n", f.readline("\u{bf}"))
+ end
+
+ File.open("text#{i+=1}", "w+b:utf-32le") do |f|
+ f.write("0123456789")
+ f.rewind
+
+ assert_equal(4, f.readline(4).bytesize)
+ assert_equal(4, f.readline(3).bytesize)
+ end
+
+ File.open("text#{i+=1}", "w+:utf-8:utf-32le") do |f|
+ f.write("0123456789")
+ f.rewind
+
+ assert_equal(4, f.readline(4).bytesize)
+ assert_equal(4, f.readline(3).bytesize)
+ end
+ end
+ end
+
def test_set_lineno_readline
pipe(proc do |w|
w.puts "foo"
@@ -2451,10 +2555,6 @@ class TestIO < Test::Unit::TestCase
end
def test_autoclose_true_closed_by_finalizer
- # http://ci.rvm.jp/results/trunk-rjit@silicon-docker/1465760
- # http://ci.rvm.jp/results/trunk-rjit@silicon-docker/1469765
- omit 'this randomly fails with RJIT' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
-
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
t = Tempfile.new(pre)
@@ -2515,36 +2615,15 @@ class TestIO < Test::Unit::TestCase
assert_equal({:a=>1}, open(o, {a: 1}))
end
- def test_open_pipe
- assert_deprecated_warning(/Kernel#open with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
- open("|" + EnvUtil.rubybin, "r+") do |f|
- f.puts "puts 'foo'"
- f.close_write
- assert_equal("foo\n", f.read)
- end
- end
- end
+ def test_path_with_pipe
+ mkcdtmpdir do
+ cmd = "|echo foo"
+ assert_file.not_exist?(cmd)
- def test_read_command
- assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
- assert_equal("foo\n", IO.read("|echo foo"))
- end
- assert_raise(Errno::ENOENT, Errno::EINVAL) do
- File.read("|#{EnvUtil.rubybin} -e puts")
- end
- assert_raise(Errno::ENOENT, Errno::EINVAL) do
- File.binread("|#{EnvUtil.rubybin} -e puts")
- end
- assert_raise(Errno::ENOENT, Errno::EINVAL) do
- Class.new(IO).read("|#{EnvUtil.rubybin} -e puts")
- end
- assert_raise(Errno::ENOENT, Errno::EINVAL) do
- Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
- end
- assert_raise(Errno::ESPIPE) do
- assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
- IO.read("|#{EnvUtil.rubybin} -e 'puts :foo'", 1, 1)
- end
+ pipe_errors = [Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EPERM]
+ assert_raise(*pipe_errors) { open(cmd, "r+") }
+ assert_raise(*pipe_errors) { IO.read(cmd) }
+ assert_raise(*pipe_errors) { IO.foreach(cmd) {|x| assert false } }
end
end
@@ -2647,6 +2726,17 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_reopen_binmode
+ f1 = File.open(__FILE__)
+ f2 = File.open(__FILE__)
+ f1.binmode
+ f1.reopen(f2)
+ assert_not_operator(f1, :binmode?)
+ ensure
+ f2.close
+ f1.close
+ end
+
def make_tempfile_for_encoding
t = make_tempfile
open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"}
@@ -2677,6 +2767,16 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_reopen_encoding_from_io
+ f1 = File.open(__FILE__, "rb:UTF-16LE")
+ f2 = File.open(__FILE__, "r:UTF-8")
+ f1.reopen(f2)
+ assert_equal(Encoding::UTF_8, f1.external_encoding)
+ ensure
+ f2.close
+ f1.close
+ end
+
def test_reopen_opt_encoding
feature7103 = '[ruby-core:47806]'
make_tempfile_for_encoding {|t|
@@ -2728,19 +2828,6 @@ class TestIO < Test::Unit::TestCase
end
def test_foreach
- a = []
-
- assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
- IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
- end
- assert_equal(["foo\n", "bar\n", "baz\n"], a)
-
- a = []
- assert_deprecated_warning(/IO process creation with a leading '\|'/) do # https://bugs.ruby-lang.org/issues/19630
- IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
- end
- assert_equal(["zot\n"], a)
-
make_tempfile {|t|
a = []
IO.foreach(t.path) {|x| a << x }
@@ -2816,10 +2903,10 @@ class TestIO < Test::Unit::TestCase
end
def test_print_separators
- EnvUtil.suppress_warning {
- $, = ':'
- $\ = "\n"
- }
+ assert_deprecated_warning(/non-nil '\$,'/) {$, = ":"}
+ assert_raise(TypeError) {$, = 1}
+ assert_deprecated_warning(/non-nil '\$\\'/) {$\ = "\n"}
+ assert_raise(TypeError) {$/ = 1}
pipe(proc do |w|
w.print('a')
EnvUtil.suppress_warning {w.print('a','b','c')}
@@ -3719,7 +3806,7 @@ __END__
end
tempfiles = []
- (0..fd_setsize+1).map {|i|
+ (0...fd_setsize).map {|i|
tempfiles << Tempfile.create("test_io_select_with_many_files")
}
@@ -4155,6 +4242,23 @@ __END__
end
end if Socket.const_defined?(:MSG_OOB)
+ def test_select_timeout
+ assert_equal(nil, IO.select(nil,nil,nil,0))
+ assert_equal(nil, IO.select(nil,nil,nil,0.0))
+ assert_raise(TypeError) { IO.select(nil,nil,nil,"invalid-timeout") }
+ assert_raise(ArgumentError) { IO.select(nil,nil,nil,-1) }
+ assert_raise(ArgumentError) { IO.select(nil,nil,nil,-0.1) }
+ assert_raise(ArgumentError) { IO.select(nil,nil,nil,-Float::INFINITY) }
+ assert_raise(RangeError) { IO.select(nil,nil,nil,Float::NAN) }
+ IO.pipe {|r, w|
+ w << "x"
+ ret = [[r], [], []]
+ assert_equal(ret, IO.select([r],nil,nil,0.1))
+ assert_equal(ret, IO.select([r],nil,nil,1))
+ assert_equal(ret, IO.select([r],nil,nil,Float::INFINITY))
+ }
+ end
+
def test_recycled_fd_close
dot = -'.'
IO.pipe do |sig_rd, sig_wr|
@@ -4266,4 +4370,55 @@ __END__
end
end
end
+
+ def test_blocking_timeout
+ assert_separately([], <<~'RUBY')
+ IO.pipe do |r, w|
+ trap(:INT) do
+ w.puts "INT"
+ end
+
+ main = Thread.current
+ thread = Thread.new do
+ # Wait until the main thread has entered `$stdin.gets`:
+ Thread.pass until main.status == 'sleep'
+
+ # Cause an interrupt while handling `$stdin.gets`:
+ Process.kill :INT, $$
+ end
+
+ r.timeout = 1
+ assert_equal("INT", r.gets.chomp)
+ rescue IO::TimeoutError
+ # Ignore - some platforms don't support interrupting `gets`.
+ ensure
+ thread&.join
+ end
+ RUBY
+ end
+
+ def test_fork_close
+ omit "fork is not supported" unless Process.respond_to?(:fork)
+
+ assert_separately([], <<~'RUBY')
+ r, w = IO.pipe
+
+ thread = Thread.new do
+ r.read
+ end
+
+ Thread.pass until thread.status == "sleep"
+
+ pid = fork do
+ r.close
+ end
+
+ w.close
+
+ status = Process.wait2(pid).last
+ thread.join
+
+ assert_predicate(status, :success?)
+ RUBY
+ end
end