ruby/test/stringio/test_stringio.rb
Nobuyoshi Nakada 348a534153 [ruby/stringio] Copy from the relocated string
When ungetting the string same as the same buffer string, extending
the buffer can move the pointer in the argument.  Reported by manun
Manu (manun) at https://hackerone.com/reports/2805165.

95c1194832
2024-11-05 05:01:03 +00:00

1040 lines
28 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
require 'stringio'
require "rbconfig/sizeof"
require_relative '../ruby/ut_eof'
class TestStringIO < Test::Unit::TestCase
include TestEOF
def open_file(content)
f = StringIO.new(content)
yield f
end
alias open_file_rw open_file
include TestEOF::Seek
def test_version
assert_kind_of(String, StringIO::VERSION)
end
def test_initialize
assert_kind_of StringIO, StringIO.new
assert_kind_of StringIO, StringIO.new('str')
assert_kind_of StringIO, StringIO.new('str', 'r+')
assert_kind_of StringIO, StringIO.new(nil)
assert_raise(ArgumentError) { StringIO.new('', 'x') }
assert_raise(ArgumentError) { StringIO.new('', 'rx') }
assert_raise(ArgumentError) { StringIO.new('', 'rbt') }
assert_raise(TypeError) { StringIO.new(Object) }
o = Object.new
def o.to_str
nil
end
assert_raise(TypeError) { StringIO.new(o) }
o = Object.new
def o.to_str
'str'
end
assert_kind_of StringIO, StringIO.new(o)
end
def test_null
io = StringIO.new(nil)
assert_nil io.gets
io.puts "abc"
assert_nil io.string
end
def test_truncate
io = StringIO.new("")
io.puts "abc"
assert_equal(0, io.truncate(0))
io.puts "def"
assert_equal("\0\0\0\0def\n", io.string, "[ruby-dev:24190]")
assert_raise(Errno::EINVAL) { io.truncate(-1) }
assert_equal(0, io.truncate(10))
assert_equal("\0\0\0\0def\n\0\0", io.string)
end
def test_seek_beyond_eof
io = StringIO.new
n = 100
io.seek(n)
io.print "last"
assert_equal("\0" * n + "last", io.string, "[ruby-dev:24194]")
end
def test_overwrite
stringio = StringIO.new
responses = ['', 'just another ruby', 'hacker']
responses.each do |resp|
stringio.puts(resp)
stringio.rewind
end
assert_equal("hacker\nother ruby\n", stringio.string, "[ruby-core:3836]")
end
def test_gets
assert_equal(nil, StringIO.new("").gets)
assert_equal("\n", StringIO.new("\n").gets)
assert_equal("a\n", StringIO.new("a\n").gets)
assert_equal("a\n", StringIO.new("a\nb\n").gets)
assert_equal("a", StringIO.new("a").gets)
assert_equal("a\n", StringIO.new("a\nb").gets)
assert_equal("abc\n", StringIO.new("abc\n\ndef\n").gets)
assert_equal("abc\n\ndef\n", StringIO.new("abc\n\ndef\n").gets(nil))
assert_equal("abc\n\n", StringIO.new("abc\n\ndef\n").gets(""))
stringio = StringIO.new("abc\n\ndef\n")
assert_equal("abc\n\n", stringio.gets(""))
assert_equal("def\n", stringio.gets(""))
assert_raise(TypeError){StringIO.new("").gets(1, 1)}
assert_nothing_raised {StringIO.new("").gets(nil, nil)}
assert_string("", Encoding::UTF_8, StringIO.new("foo").gets(0))
end
def test_gets_utf_16
stringio = StringIO.new("line1\nline2\nline3\n".encode("utf-16le"))
assert_equal("line1\n".encode("utf-16le"), stringio.gets)
assert_equal("line2\n".encode("utf-16le"), stringio.gets)
assert_equal("line3\n".encode("utf-16le"), stringio.gets)
assert_nil(stringio.gets)
end
def test_gets_chomp
assert_equal(nil, StringIO.new("").gets(chomp: true))
assert_equal("", StringIO.new("\n").gets(chomp: true))
assert_equal("a", StringIO.new("a\n").gets(chomp: true))
assert_equal("a", StringIO.new("a\nb\n").gets(chomp: true))
assert_equal("a", StringIO.new("a").gets(chomp: true))
assert_equal("a", StringIO.new("a\nb").gets(chomp: true))
assert_equal("abc", StringIO.new("abc\n\ndef\n").gets(chomp: true))
assert_equal("abc\n\ndef\n", StringIO.new("abc\n\ndef\n").gets(nil, chomp: true))
assert_equal("abc", StringIO.new("abc\n\ndef\n").gets("", chomp: true))
stringio = StringIO.new("abc\n\ndef\n")
assert_equal("abc", stringio.gets("", chomp: true))
assert_equal("def\n", stringio.gets("", chomp: true))
assert_string("", Encoding::UTF_8, StringIO.new("\n").gets(chomp: true))
assert_equal("", StringIO.new("ab").gets("ab", chomp: true))
end
def test_gets_chomp_eol
assert_equal(nil, StringIO.new("").gets(chomp: true))
assert_equal("", StringIO.new("\r\n").gets(chomp: true))
assert_equal("a", StringIO.new("a\r\n").gets(chomp: true))
assert_equal("a", StringIO.new("a\r\nb\r\n").gets(chomp: true))
assert_equal("a", StringIO.new("a").gets(chomp: true))
assert_equal("a", StringIO.new("a\r\nb").gets(chomp: true))
assert_equal("abc", StringIO.new("abc\r\n\r\ndef\r\n").gets(chomp: true))
assert_equal("abc\r\n\r\ndef\r\n", StringIO.new("abc\r\n\r\ndef\r\n").gets(nil, chomp: true))
assert_equal("abc", StringIO.new("abc\r\n\r\ndef\r\n").gets("", chomp: true))
stringio = StringIO.new("abc\r\n\r\ndef\r\n")
assert_equal("abc", stringio.gets("", chomp: true))
assert_equal("def\r\n", stringio.gets("", chomp: true))
end
def test_readlines
assert_equal([], StringIO.new("").readlines)
assert_equal(["\n"], StringIO.new("\n").readlines)
assert_equal(["a\n"], StringIO.new("a\n").readlines)
assert_equal(["a\n", "b\n"], StringIO.new("a\nb\n").readlines)
assert_equal(["a"], StringIO.new("a").readlines)
assert_equal(["a\n", "b"], StringIO.new("a\nb").readlines)
assert_equal(["abc\n", "\n", "def\n"], StringIO.new("abc\n\ndef\n").readlines)
assert_equal(["abc\n\ndef\n"], StringIO.new("abc\n\ndef\n").readlines(nil), "[ruby-dev:34591]")
assert_equal(["abc\n\n", "def\n"], StringIO.new("abc\n\ndef\n").readlines(""))
end
def test_write
s = ""
f = StringIO.new(s, "w")
f.print("foo")
f.close
assert_equal("foo", s)
f = StringIO.new(s, File::WRONLY)
f.print("bar")
f.close
assert_equal("bar", s)
f = StringIO.new(s, "a")
o = Object.new
def o.to_s; "baz"; end
f.print(o)
f.close
assert_equal("barbaz", s)
ensure
f.close unless f.closed?
end
def test_write_nonblock_no_exceptions
s = ""
f = StringIO.new(s, "w")
f.write_nonblock("foo", exception: false)
f.close
assert_equal("foo", s)
end
def test_write_nonblock
s = ""
f = StringIO.new(s, "w")
f.write_nonblock("foo")
f.close
assert_equal("foo", s)
f = StringIO.new(s, File::WRONLY)
f.write_nonblock("bar")
f.close
assert_equal("bar", s)
f = StringIO.new(s, "a")
o = Object.new
def o.to_s; "baz"; end
f.write_nonblock(o)
f.close
assert_equal("barbaz", s)
ensure
f.close unless f.closed?
end
def test_write_encoding
s = "".force_encoding(Encoding::UTF_8)
f = StringIO.new(s)
f.print("\u{3053 3093 306b 3061 306f ff01}".b)
assert_equal(Encoding::UTF_8, s.encoding, "honor the original encoding over ASCII-8BIT")
end
def test_write_encoding_conversion
convertible = "\u{3042}"
inconvertible = "\u{1f363}"
conversion_encoding = Encoding::Windows_31J
s = StringIO.new.set_encoding(conversion_encoding)
s.write(convertible)
assert_equal(conversion_encoding, s.string.encoding)
s = StringIO.new.set_encoding(Encoding::UTF_8)
s.write("foo".force_encoding("ISO-8859-1"), convertible)
assert_equal(Encoding::UTF_8, s.string.encoding)
s = StringIO.new.set_encoding(Encoding::US_ASCII)
s.write("foo".force_encoding("US-ASCII"), convertible)
assert_equal(Encoding::UTF_8, s.string.encoding)
all_assertions do |a|
[
inconvertible,
convertible + inconvertible,
[convertible, inconvertible],
["a", inconvertible],
].each do |data|
a.for(data.inspect) do
s = StringIO.new.set_encoding(conversion_encoding)
assert_raise(Encoding::CompatibilityError) do
s.write(*data)
end
end
end
end
end
def test_write_integer_overflow
f = StringIO.new
f.pos = StringIO::MAX_LENGTH
assert_raise(ArgumentError) {
f.write("pos + len overflows")
}
end
def test_write_with_multiple_arguments
s = ""
f = StringIO.new(s, "w")
f.write("foo", "bar")
f.close
assert_equal("foobar", s)
ensure
f.close unless f.closed?
end
def test_set_encoding
bug10285 = '[ruby-core:65240] [Bug #10285]'
f = StringIO.new()
f.set_encoding(Encoding::ASCII_8BIT)
f.write("quz \x83 mat".b)
s = "foo \x97 bar".force_encoding(Encoding::WINDOWS_1252)
assert_nothing_raised(Encoding::CompatibilityError, bug10285) {
f.write(s)
}
assert_equal(Encoding::ASCII_8BIT, f.string.encoding, bug10285)
bug11827 = '[ruby-core:72189] [Bug #11827]'
f = StringIO.new("foo\x83".freeze)
assert_nothing_raised(RuntimeError, bug11827) {
f.set_encoding(Encoding::ASCII_8BIT)
}
assert_equal("foo\x83".b, f.gets)
f = StringIO.new()
f.set_encoding("ISO-8859-16:ISO-8859-1")
assert_equal(Encoding::ISO_8859_16, f.external_encoding)
assert_equal(Encoding::ISO_8859_16, f.string.encoding)
assert_nil(f.internal_encoding)
end
def test_mode_error
f = StringIO.new("", "r")
assert_raise(IOError) { f.write("foo") }
f = StringIO.new("", "w")
assert_raise(IOError) { f.read }
assert_raise(Errno::EACCES) { StringIO.new("".freeze, "w") }
s = ""
f = StringIO.new(s, "w")
s.freeze
assert_raise(IOError) { f.write("foo") }
assert_raise(IOError) { StringIO.allocate.read }
ensure
f.close unless f.closed?
end
def test_open
s = ""
StringIO.open("foo") {|f| s = f.read }
assert_equal("foo", s)
end
def test_isatty
assert_equal(false, StringIO.new("").isatty)
end
def test_fsync
assert_equal(0, StringIO.new("").fsync)
end
def test_sync
assert_equal(true, StringIO.new("").sync)
assert_equal(false, StringIO.new("").sync = false)
end
def test_set_fcntl
assert_raise(NotImplementedError) { StringIO.new("").fcntl }
end
def test_close
f = StringIO.new("")
f.close
assert_nil(f.close)
f = StringIO.new("")
f.close_read
f.close_write
assert_nil(f.close)
ensure
f.close unless f.closed?
end
def test_close_read
f = StringIO.new("")
f.close_read
assert_raise(IOError) { f.read }
assert_nothing_raised(IOError) {f.close_read}
f.close
f = StringIO.new("", "w")
assert_raise(IOError) { f.close_read }
f.close
ensure
f.close unless f.closed?
end
def test_close_write
f = StringIO.new("")
f.close_write
assert_raise(IOError) { f.write("foo") }
assert_nothing_raised(IOError) {f.close_write}
f.close
f = StringIO.new("", "r")
assert_raise(IOError) { f.close_write }
f.close
ensure
f.close unless f.closed?
end
def test_closed
f = StringIO.new("")
assert_equal(false, f.closed?)
f.close
assert_equal(true, f.closed?)
ensure
f.close unless f.closed?
end
def test_closed_read
f = StringIO.new("")
assert_equal(false, f.closed_read?)
f.close_write
assert_equal(false, f.closed_read?)
f.close_read
assert_equal(true, f.closed_read?)
ensure
f.close unless f.closed?
end
def test_closed_write
f = StringIO.new("")
assert_equal(false, f.closed_write?)
f.close_read
assert_equal(false, f.closed_write?)
f.close_write
assert_equal(true, f.closed_write?)
ensure
f.close unless f.closed?
end
def test_dup
f1 = StringIO.new("1234")
assert_equal("1", f1.getc)
f2 = f1.dup
assert_equal("2", f2.getc)
assert_equal("3", f1.getc)
assert_equal("4", f2.getc)
assert_equal(nil, f1.getc)
assert_equal(true, f2.eof?)
f1.close
assert_equal(false, f2.closed?, '[ruby-core:48443]')
ensure
f1.close unless f1.closed?
f2.close unless f2.closed?
end
def test_lineno
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal([0, "foo\n"], [f.lineno, f.gets])
assert_equal([1, "bar\n"], [f.lineno, f.gets])
f.lineno = 1000
assert_equal([1000, "baz\n"], [f.lineno, f.gets])
assert_equal([1001, nil], [f.lineno, f.gets])
ensure
f.close unless f.closed?
end
def test_pos
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal([0, "foo\n"], [f.pos, f.gets])
assert_equal([4, "bar\n"], [f.pos, f.gets])
assert_raise(Errno::EINVAL) { f.pos = -1 }
f.pos = 1
assert_equal([1, "oo\n"], [f.pos, f.gets])
assert_equal([4, "bar\n"], [f.pos, f.gets])
assert_equal([8, "baz\n"], [f.pos, f.gets])
assert_equal([12, nil], [f.pos, f.gets])
ensure
f.close unless f.closed?
end
def test_reopen
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal("foo\n", f.gets)
f.reopen("qux\nquux\nquuux\n")
assert_equal("qux\n", f.gets)
f2 = StringIO.new("")
f2.reopen(f)
assert_equal("quux\n", f2.gets)
ensure
f.close unless f.closed?
end
def test_seek
f = StringIO.new("1234")
assert_raise(Errno::EINVAL) { f.seek(-1) }
f.seek(-1, 2)
assert_equal("4", f.getc)
assert_raise(Errno::EINVAL) { f.seek(1, 3) }
f.close
assert_raise(IOError) { f.seek(0) }
ensure
f.close unless f.closed?
end
def test_each_byte
f = StringIO.new("1234")
a = []
f.each_byte {|c| a << c }
assert_equal(%w(1 2 3 4).map {|c| c.ord }, a)
ensure
f.close unless f.closed?
end
def test_each_byte_closed
f = StringIO.new("1234")
assert_equal("1".ord, f.each_byte {|c| f.close; break c })
f = StringIO.new("1234")
assert_raise(IOError) do
f.each_byte { f.close }
end
end
def test_getbyte
f = StringIO.new("1234")
assert_equal("1".ord, f.getbyte)
assert_equal("2".ord, f.getbyte)
assert_equal("3".ord, f.getbyte)
assert_equal("4".ord, f.getbyte)
assert_equal(nil, f.getbyte)
ensure
f.close unless f.closed?
end
def test_ungetbyte
s = "foo\nbar\n"
t = StringIO.new(s, "r")
t.ungetbyte(0x41)
assert_equal(0x41, t.getbyte)
t.ungetbyte("qux")
assert_equal("quxfoo\n", t.gets)
t.set_encoding("utf-8")
t.ungetbyte(0x89)
t.ungetbyte(0x8e)
t.ungetbyte("\xe7")
t.ungetbyte("\xe7\xb4\x85")
assert_equal("\u7d05\u7389bar\n", t.gets)
assert_equal("q\u7d05\u7389bar\n", s)
t.pos = 1
t.ungetbyte("\u{30eb 30d3 30fc}")
assert_equal(0, t.pos)
assert_equal("\u{30eb 30d3 30fc}\u7d05\u7389bar\n", s)
assert_nothing_raised {t.ungetbyte(-1)}
assert_nothing_raised {t.ungetbyte(256)}
assert_nothing_raised {t.ungetbyte(1<<64)}
end
def test_ungetc
s = "1234"
f = StringIO.new(s, "r")
assert_nothing_raised { f.ungetc("x") }
assert_equal("x", f.getc) # bug? -> it's a feature from 1.9.
assert_equal("1", f.getc)
s = "1234"
f = StringIO.new(s, "r")
assert_equal("1", f.getc)
f.ungetc("y".ord)
assert_equal("y", f.getc)
assert_equal("2", f.getc)
assert_raise(RangeError) {f.ungetc(0x1ffffff)}
assert_raise(RangeError) {f.ungetc(0xffffffffffffff)}
ensure
f.close unless f.closed?
end
def test_readchar
f = StringIO.new("1234")
a = ""
assert_raise(EOFError) { loop { a << f.readchar } }
assert_equal("1234", a)
end
def test_readbyte
f = StringIO.new("1234")
a = []
assert_raise(EOFError) { loop { a << f.readbyte } }
assert_equal("1234".unpack("C*"), a)
end
def test_each_char
f = StringIO.new("1234")
assert_equal(%w(1 2 3 4), f.each_char.to_a)
end
def test_each_char_closed
f = StringIO.new("1234")
assert_equal("1", f.each_char {|c| f.close; break c })
f = StringIO.new("1234")
assert_raise(IOError) do
f.each_char { f.close }
end
end
def test_each_codepoint
f = StringIO.new("1234")
assert_equal([49, 50, 51, 52], f.each_codepoint.to_a)
end
def test_each_codepoint_closed
f = StringIO.new("1234")
assert_equal("1".ord, f.each_codepoint {|c| f.close; break c })
f = StringIO.new("1234")
assert_raise(IOError) do
f.each_codepoint { f.close }
end
end
def test_each_codepoint_enumerator
io = StringIO.new('你好построить')
chinese_part = io.each_codepoint.take(2).pack('U*')
russian_part = io.read(40).force_encoding('UTF-8')
assert_equal("你好", chinese_part)
assert_equal("построить", russian_part)
end
def test_gets2
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal("fo", f.gets(2))
o = Object.new
def o.to_str; "z"; end
assert_equal("o\nbar\nbaz", f.gets(o))
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal("foo\nbar\nbaz", f.gets("az"))
f = StringIO.new("a" * 10000 + "zz!")
assert_equal("a" * 10000 + "zz", f.gets("zz"))
f = StringIO.new("a" * 10000 + "zz!")
assert_equal("a" * 10000 + "zz!", f.gets("zzz"))
bug4112 = '[ruby-dev:42674]'
["a".encode("utf-16be"), "\u3042"].each do |s|
assert_equal(s, StringIO.new(s).gets(1), bug4112)
assert_equal(s, StringIO.new(s).gets(nil, 1), bug4112)
end
end
def test_each_string_sep
f = StringIO.new('a||b||c')
assert_equal(["a||", "b||", "c"], f.each("||").to_a)
f.rewind
assert_equal(["a", "b", "c"], f.each("||", chomp: true).to_a)
end
def test_each
f = StringIO.new("foo\nbar\nbaz\n")
assert_equal(["foo\n", "bar\n", "baz\n"], f.each.to_a)
f.rewind
assert_equal(["foo", "bar", "baz"], f.each(chomp: true).to_a)
f = StringIO.new("foo\nbar\n\n\nbaz\n")
assert_equal(["foo\nbar\n\n\n", "baz\n"], f.each("").to_a)
f.rewind
assert_equal(["foo\nbar", "baz\n"], f.each("", chomp: true).to_a)
f = StringIO.new("foo\r\nbar\r\n\r\n\r\nbaz\r\n")
assert_equal(["foo\r\nbar\r\n\r\n\r\n", "baz\r\n"], f.each("").to_a)
f.rewind
assert_equal(["foo\r\nbar", "baz\r\n"], f.each("", chomp: true).to_a)
f = StringIO.new("abc\n\ndef\n")
assert_equal(["ab", "c\n", "\nd", "ef", "\n"], f.each(nil, 2, chomp: true).to_a)
end
def test_putc
s = ""
f = StringIO.new(s, "w")
f.putc("1")
f.putc("2")
f.putc("3")
f.close
assert_equal("123", s)
s = "foo"
f = StringIO.new(s, "a")
f.putc("1")
f.putc("2")
f.putc("3")
f.close
assert_equal("foo123", s)
end
def test_putc_nonascii
s = ""
f = StringIO.new(s, "w")
f.putc("\u{3042}")
f.putc(0x3044)
f.close
assert_equal("\u{3042}D", s)
s = "foo"
f = StringIO.new(s, "a")
f.putc("\u{3042}")
f.putc(0x3044)
f.close
assert_equal("foo\u{3042}D", s)
end
def test_read
f = StringIO.new("\u3042\u3044")
assert_raise(ArgumentError) { f.read(-1) }
assert_raise(ArgumentError) { f.read(1, 2, 3) }
assert_equal("\u3042\u3044", f.read)
assert_nil(f.read(1))
f.rewind
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read(f.size))
bug5207 = '[ruby-core:39026]'
f.rewind
assert_equal("\u3042\u3044", f.read(nil, nil), bug5207)
f.rewind
s = ""
assert_same(s, f.read(nil, s))
assert_equal("\u3042\u3044", s, bug5207)
f.rewind
# not empty buffer
s = "0123456789"
assert_same(s, f.read(nil, s))
assert_equal("\u3042\u3044", s)
bug13806 = '[ruby-core:82349] [Bug #13806]'
assert_string("", Encoding::UTF_8, f.read, bug13806)
assert_string("", Encoding::UTF_8, f.read(nil, nil), bug13806)
s.force_encoding(Encoding::US_ASCII)
assert_same(s, f.read(nil, s))
assert_string("", Encoding::UTF_8, s, bug13806)
bug20418 = '[Bug #20418] ™€®'.b
f = StringIO.new(bug20418)
s = ""
assert_equal(Encoding::UTF_8, s.encoding, bug20418)
f.read(4, s)
assert_equal(Encoding::UTF_8, s.encoding, bug20418)
f.rewind
s = ""
f.read(nil, s)
assert_equal(Encoding::ASCII_8BIT, s.encoding, bug20418)
end
def test_readpartial
f = StringIO.new("\u3042\u3044")
assert_raise(ArgumentError) { f.readpartial(-1) }
assert_raise(ArgumentError) { f.readpartial(1, 2, 3) }
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(100))
f.rewind
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size))
f.rewind
# not empty buffer
s = '0123456789'.b
assert_equal("\u3042\u3044".b, f.readpartial(f.size, s))
end
def test_read_nonblock
f = StringIO.new("\u3042\u3044")
assert_raise(ArgumentError) { f.read_nonblock(-1) }
assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3) }
assert_equal("\u3042\u3044".force_encoding("BINARY"), f.read_nonblock(100))
assert_raise(EOFError) { f.read_nonblock(10) }
f.rewind
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
end
def test_read_nonblock_no_exceptions
f = StringIO.new("\u3042\u3044")
assert_raise(ArgumentError) { f.read_nonblock(-1, exception: false) }
assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3, exception: false) }
assert_raise(ArgumentError) { f.read_nonblock }
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(100, exception: false))
assert_equal(nil, f.read_nonblock(10, exception: false))
f.rewind
assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
f.rewind
# not empty buffer
s = '0123456789'.b
assert_equal("\u3042\u3044".b, f.read_nonblock(f.size, s))
end
def test_sysread
f = StringIO.new("sysread \u{30c6 30b9 30c8}")
assert_equal "sysread \u{30c6 30b9 30c8}", f.sysread
assert_equal "", f.sysread
assert_raise(EOFError) { f.sysread(1) }
f.rewind
assert_equal Encoding::ASCII_8BIT, f.sysread(3).encoding
end
def test_pread
f = StringIO.new("pread")
f.read
assert_equal "pre".b, f.pread(3, 0)
assert_equal "read".b, f.pread(4, 1)
assert_equal Encoding::ASCII_8BIT, f.pread(4, 1).encoding
buf = "".b
f.pread(3, 0, buf)
assert_equal "pre".b, buf
f.pread(4, 1, buf)
assert_equal "read".b, buf
assert_raise(EOFError) { f.pread(1, 5) }
assert_raise(ArgumentError) { f.pread(-1, 0) }
assert_raise(Errno::EINVAL) { f.pread(3, -1) }
assert_equal "".b, StringIO.new("").pread(0, 0)
assert_equal "".b, StringIO.new("").pread(0, -10)
buf = "stale".b
assert_equal "stale".b, StringIO.new("").pread(0, 0, buf)
assert_equal "stale".b, buf
end
def test_size
f = StringIO.new("1234")
assert_equal(4, f.size)
end
# This test is should in ruby/test_method.rb
# However this test depends on stringio library,
# we write it here.
class C < StringIO
alias old_init initialize
attr_reader :foo
def initialize
@foo = :ok
old_init
end
end
def test_method
assert_equal(:ok, C.new.foo, 'Bug #632 [ruby-core:19282]')
end
def test_ungetc_pos
b = '\\b00010001 \\B00010001 \\b1 \\B1 \\b000100011'
s = StringIO.new( b )
expected_pos = 0
while n = s.getc
assert_equal( expected_pos + 1, s.pos )
s.ungetc( n )
assert_equal( expected_pos, s.pos )
assert_equal( n, s.getc )
expected_pos += 1
end
end
def test_ungetc_padding
s = StringIO.new()
s.pos = 2
s.ungetc("a")
assert_equal("\0""a", s.string)
s.pos = 0
s.ungetc("b")
assert_equal("b""\0""a", s.string)
end
def test_ungetc_fill
count = 100
s = StringIO.new
s.print 'a' * count
s.ungetc('b' * (count * 5))
assert_equal((count * 5), s.string.size)
assert_match(/\Ab+\z/, s.string)
end
def test_ungetc_same_string
s = StringIO.new("abc" * 30)
s.ungetc(s.string)
assert_match(/\A(?:abc){60}\z/, s.string)
s = StringIO.new("abc" * 30)
s.pos = 70 # ("abc".size * 30 - 70).divmod(3) == [6, 2]
s.ungetc(s.string)
assert_match(/\A(?:abc){30}bc(?:abc){6}\z/, s.string)
end
def test_ungetbyte_pos
b = '\\b00010001 \\B00010001 \\b1 \\B1 \\b000100011'
s = StringIO.new( b )
expected_pos = 0
while n = s.getbyte
assert_equal( expected_pos + 1, s.pos )
s.ungetbyte( n )
assert_equal( expected_pos, s.pos )
assert_equal( n, s.getbyte )
expected_pos += 1
end
end
def test_ungetbyte_padding
s = StringIO.new()
s.pos = 2
s.ungetbyte("a".ord)
assert_equal("\0""a", s.string)
s.pos = 0
s.ungetbyte("b".ord)
assert_equal("b""\0""a", s.string)
end
def test_ungetbyte_fill
count = 100
s = StringIO.new
s.print 'a' * count
s.ungetbyte('b' * (count * 5))
assert_equal((count * 5), s.string.size)
assert_match(/\Ab+\z/, s.string)
end
def test_ungetbyte_same_string
s = StringIO.new("abc" * 30)
s.ungetc(s.string)
assert_match(/\A(?:abc){60}\z/, s.string)
s = StringIO.new("abc" * 30)
s.pos = 70 # ("abc".size * 30 - 70).divmod(3) == [6, 2]
s.ungetbyte(s.string)
assert_match(/\A(?:abc){30}bc(?:abc){6}\z/, s.string)
end
def test_frozen
s = StringIO.new
s.freeze
bug = '[ruby-core:33648]'
exception_class = defined?(FrozenError) ? FrozenError : RuntimeError
assert_raise(exception_class, bug) {s.puts("foo")}
assert_raise(exception_class, bug) {s.string = "foo"}
assert_raise(exception_class, bug) {s.reopen("")}
end
def test_frozen_string
s = StringIO.new("".freeze)
bug = '[ruby-core:48530]'
assert_raise(IOError, bug) {s.write("foo")}
assert_raise(IOError, bug) {s.ungetc("a")}
assert_raise(IOError, bug) {s.ungetbyte("a")}
end
def test_readlines_limit_0
assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.readlines(0) }
end
def test_each_line_limit_0
assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.each_line(0){} }
assert_raise(ArgumentError, "[ruby-dev:43392]") { StringIO.new.each_line("a",0){} }
end
def test_binmode
s = StringIO.new
s.set_encoding('utf-8')
assert_same s, s.binmode
bug_11945 = '[ruby-core:72699] [Bug #11945]'
assert_equal Encoding::ASCII_8BIT, s.external_encoding, bug_11945
end
def test_new_block_warning
assert_warn(/does not take block/) do
StringIO.new {}
end
end
def test_overflow
intptr_max = RbConfig::LIMITS["INTPTR_MAX"]
return if intptr_max > StringIO::MAX_LENGTH
limit = intptr_max - 0x10
assert_separately(%w[-rstringio], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
limit = #{limit}
ary = []
begin
x = "a"*0x100000
break if [x].pack("p").unpack("i!")[0] < 0
ary << x
end while ary.size <= 100
s = StringIO.new(x)
s.gets("xxx", limit)
assert_equal(0x100000, s.pos)
end;
end
def test_encoding_write
s = StringIO.new("", "w:utf-32be")
s.print "abc"
assert_equal("abc".encode("utf-32be"), s.string)
end
def test_encoding_read
s = StringIO.new("abc".encode("utf-32be"), "r:utf-8")
assert_equal("\0\0\0a\0\0\0b\0\0\0c", s.read)
end
%w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name|
define_method("test_strip_bom:#{name}") do
text = "\uFEFF\u0100a"
content = text.encode(name)
result = StringIO.new(content, mode: 'rb:BOM|UTF-8').read
assert_equal(Encoding.find(name), result.encoding, name)
assert_equal(content[1..-1].b, result.b, name)
StringIO.open(content) {|f|
assert_equal(Encoding.find(name), f.set_encoding_by_bom)
}
end
end
def test_binary_encoding_read_and_default_internal
verbose, $VERBOSE = $VERBOSE, nil
default_internal = Encoding.default_internal
Encoding.default_internal = Encoding::UTF_8
$VERBOSE = verbose
assert_equal Encoding::BINARY, StringIO.new("Hello".b).read.encoding
ensure
$VERBOSE = nil
Encoding.default_internal = default_internal
$VERBOSE = verbose
end
def test_coderange_after_overwrite
s = StringIO.new("".b)
s.write("a=b&c=d")
s.rewind
assert_predicate(s.string, :ascii_only?)
s.write "\u{431 43e 433 443 441}"
assert_not_predicate(s.string, :ascii_only?)
s = StringIO.new("\u{3042}")
s.rewind
assert_not_predicate(s.string, :ascii_only?)
s.write('aaaa')
assert_predicate(s.string, :ascii_only?)
end
require "objspace"
if ObjectSpace.respond_to?(:dump) && ObjectSpace.dump(eval(%{"test"})).include?('"chilled":true') # Ruby 3.4+ chilled strings
def test_chilled_string
chilled_string = eval(%{""})
io = StringIO.new(chilled_string)
assert_warning(/literal string will be frozen/) { io << "test" }
assert_equal("test", io.string)
assert_same(chilled_string, io.string)
end
def test_chilled_string_string_set
io = StringIO.new
chilled_string = eval(%{""})
io.string = chilled_string
assert_warning(/literal string will be frozen/) { io << "test" }
assert_equal("test", io.string)
assert_same(chilled_string, io.string)
end
end
private
def assert_string(content, encoding, str, mesg = nil)
assert_equal([content, encoding], [str, str.encoding], mesg)
end
end