diff options
Diffstat (limited to 'test/stringio/test_stringio.rb')
| -rw-r--r-- | test/stringio/test_stringio.rb | 624 |
1 files changed, 607 insertions, 17 deletions
diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 0eceeba894..024906261e 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: false require 'test/unit' require 'stringio' +require "rbconfig/sizeof" require_relative '../ruby/ut_eof' class TestStringIO < Test::Unit::TestCase @@ -12,14 +14,95 @@ class TestStringIO < Test::Unit::TestCase include TestEOF::Seek + def test_do_not_mutate_shared_buffers + # Ensure we have two strings that are not embedded but have the same shared + # string reference. + # + # In this case, we must use eval because we need two strings literals that + # are long enough they cannot be embedded, but also contain the same bytes. + + a = eval("+"+("x" * 1024).dump) + b = eval("+"+("x" * 1024).dump) + + s = StringIO.new(b) + s.getc + s.ungetc '#' + + # We mutated b, so a should not be mutated + assert_equal("x", a[0]) + end + + 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 + + # Null device StringIO just drop ungot string + io.ungetc '#' + assert_nil io.getc + end + + def test_eof_null + io = StringIO.new(nil) + assert_predicate io, :eof? + end + + def test_pread_null + io = StringIO.new(nil) + assert_raise(EOFError) { io.pread(1, 0) } + end + + def test_read_null + io = StringIO.new(nil) + assert_equal "", io.read(0) + end + + def test_seek_null + io = StringIO.new(nil) + assert_equal(0, io.seek(0, IO::SEEK_SET)) + assert_equal(0, io.pos) + assert_equal(0, io.seek(0, IO::SEEK_CUR)) + assert_equal(0, io.pos) + assert_equal(0, io.seek(0, IO::SEEK_END)) # This should not segfault + assert_equal(0, io.pos) + end + def test_truncate io = StringIO.new("") io.puts "abc" - io.truncate(0) + 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) } - io.truncate(10) + assert_equal(0, io.truncate(10)) assert_equal("\0\0\0\0def\n\0\0", io.string) end @@ -51,8 +134,55 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -89,6 +219,14 @@ class TestStringIO < Test::Unit::TestCase 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") @@ -111,6 +249,90 @@ class TestStringIO < Test::Unit::TestCase 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") } @@ -155,12 +377,12 @@ class TestStringIO < Test::Unit::TestCase def test_close f = StringIO.new("") f.close - assert_raise(IOError) { f.close } + assert_nil(f.close) f = StringIO.new("") f.close_read f.close_write - assert_raise(IOError) { f.close } + assert_nil(f.close) ensure f.close unless f.closed? end @@ -169,7 +391,7 @@ class TestStringIO < Test::Unit::TestCase f = StringIO.new("") f.close_read assert_raise(IOError) { f.read } - assert_raise(IOError) { f.close_read } + assert_nothing_raised(IOError) {f.close_read} f.close f = StringIO.new("", "w") @@ -183,7 +405,7 @@ class TestStringIO < Test::Unit::TestCase f = StringIO.new("") f.close_write assert_raise(IOError) { f.write("foo") } - assert_raise(IOError) { f.close_write } + assert_nothing_raised(IOError) {f.close_write} f.close f = StringIO.new("", "r") @@ -290,6 +512,11 @@ class TestStringIO < Test::Unit::TestCase f.close unless f.closed? end + def test_seek_frozen_string + f = StringIO.new(-"1234") + assert_equal(0, f.seek(1)) + end + def test_each_byte f = StringIO.new("1234") a = [] @@ -299,6 +526,15 @@ class TestStringIO < Test::Unit::TestCase 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) @@ -323,6 +559,15 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -338,6 +583,9 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -361,11 +609,39 @@ class TestStringIO < Test::Unit::TestCase 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)) @@ -388,9 +664,30 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -411,11 +708,28 @@ class TestStringIO < Test::Unit::TestCase 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)) @@ -424,39 +738,105 @@ class TestStringIO < Test::Unit::TestCase assert_equal("\u3042\u3044", f.read(nil, nil), bug5207) f.rewind s = "" - f.read(nil, s) + assert_same(s, f.read(nil, s)) assert_equal("\u3042\u3044", s, bug5207) f.rewind # not empty buffer s = "0123456789" - f.read(nil, s) + 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", f.readpartial) + 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' - assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size, s)) + 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", f.read_nonblock) + 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' - assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size, s)) + 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 @@ -495,13 +875,89 @@ class TestStringIO < Test::Unit::TestCase 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]' - assert_raise(RuntimeError, bug) {s.puts("foo")} - assert_raise(RuntimeError, bug) {s.string = "foo"} - assert_raise(RuntimeError, bug) {s.reopen("")} + 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 @@ -520,4 +976,138 @@ class TestStringIO < Test::Unit::TestCase 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[-W0 -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 + + def test_coderange_after_read_into_buffer + s = StringIO.new("01234567890".b) + + buf = "¿Cómo estás? Ça va bien?" + assert_not_predicate(buf, :ascii_only?) + + assert_predicate(s.string, :ascii_only?) + + s.read(10, buf) + + assert_predicate(buf, :ascii_only?) + assert_equal '0123456789', buf + 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 + + def test_chilled_string_set_enocoding + chilled_string = eval(%{""}) + io = StringIO.new(chilled_string) + assert_warning("") { io.set_encoding(Encoding::BINARY) } + 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 |
