diff options
Diffstat (limited to 'test/stringio')
| -rw-r--r-- | test/stringio/test_ractor.rb | 8 | ||||
| -rw-r--r-- | test/stringio/test_stringio.rb | 249 |
2 files changed, 235 insertions, 22 deletions
diff --git a/test/stringio/test_ractor.rb b/test/stringio/test_ractor.rb index 1c334e2c3f..6acf53fb0a 100644 --- a/test/stringio/test_ractor.rb +++ b/test/stringio/test_ractor.rb @@ -8,16 +8,20 @@ class TestStringIOInRactor < Test::Unit::TestCase def test_ractor assert_in_out_err([], <<-"end;", ["true"], []) + class Ractor + alias value take unless method_defined? :value # compat with Ruby 3.4 and olders + end + require "stringio" $VERBOSE = nil r = Ractor.new do - io = StringIO.new("") + io = StringIO.new(+"") io.puts "abc" io.truncate(0) io.puts "def" "\0\0\0\0def\n" == io.string end - puts r.take + puts r.value end; end end diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 8afbcf3545..0f61245a8a 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -14,6 +14,24 @@ 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 @@ -22,10 +40,11 @@ class TestStringIO < Test::Unit::TestCase 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(nil) } + assert_raise(TypeError) { StringIO.new(Object) } o = Object.new def o.to_str @@ -40,6 +59,42 @@ class TestStringIO < Test::Unit::TestCase 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" @@ -237,7 +292,7 @@ class TestStringIO < Test::Unit::TestCase def test_write_integer_overflow f = StringIO.new - f.pos = RbConfig::LIMITS["LONG_MAX"] + f.pos = StringIO::MAX_LENGTH assert_raise(ArgumentError) { f.write("pos + len overflows") } @@ -304,6 +359,9 @@ class TestStringIO < Test::Unit::TestCase def test_isatty assert_equal(false, StringIO.new("").isatty) + assert_equal(false, StringIO.new("").tty?) + assert_nothing_raised { StringIO.new("").freeze.isatty} + assert_nothing_raised { StringIO.new("").freeze.tty?} end def test_fsync @@ -313,6 +371,7 @@ class TestStringIO < Test::Unit::TestCase def test_sync assert_equal(true, StringIO.new("").sync) assert_equal(false, StringIO.new("").sync = false) + assert_nothing_raised { StringIO.new("").freeze.sync} end def test_set_fcntl @@ -365,8 +424,8 @@ class TestStringIO < Test::Unit::TestCase assert_equal(false, f.closed?) f.close assert_equal(true, f.closed?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed? } end def test_closed_read @@ -376,8 +435,8 @@ class TestStringIO < Test::Unit::TestCase assert_equal(false, f.closed_read?) f.close_read assert_equal(true, f.closed_read?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed_read? } end def test_closed_write @@ -387,8 +446,8 @@ class TestStringIO < Test::Unit::TestCase assert_equal(false, f.closed_write?) f.close_write assert_equal(true, f.closed_write?) - ensure - f.close unless f.closed? + f.freeze + assert_nothing_raised { f.closed_write? } end def test_dup @@ -414,8 +473,8 @@ class TestStringIO < Test::Unit::TestCase 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? + f.freeze + assert_nothing_raised { f.lineno } end def test_pos @@ -428,8 +487,8 @@ class TestStringIO < Test::Unit::TestCase 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? + f.freeze + assert_nothing_raised { f.pos } end def test_reopen @@ -453,6 +512,8 @@ class TestStringIO < Test::Unit::TestCase assert_raise(Errno::EINVAL) { f.seek(1, 3) } f.close assert_raise(IOError) { f.seek(0) } + f = StringIO.new(-"1234") + assert_equal(0, f.seek(1)) ensure f.close unless f.closed? end @@ -692,6 +753,20 @@ class TestStringIO < Test::Unit::TestCase 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) + + assert_raise(ArgumentError) {f.read(1, f.string)} end def test_readpartial @@ -703,8 +778,8 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -728,8 +803,8 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -757,19 +832,28 @@ class TestStringIO < Test::Unit::TestCase assert_raise(EOFError) { f.pread(1, 5) } assert_raise(ArgumentError) { f.pread(-1, 0) } + assert_raise(ArgumentError) { f.pread(0, 0, f.string) } assert_raise(Errno::EINVAL) { f.pread(3, -1) } + assert_raise(Errno::EINVAL) { f.pread(0, -1) } + assert_raise(IOError) { StringIO.new(nil, "w").pread(3, 0) } + assert_raise(TypeError) { f.pread(3, 0, []) } 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 + + assert_nothing_raised { StringIO.new("pread").freeze.pread(3, 0)} end def test_size f = StringIO.new("1234") assert_equal(4, f.size) + assert_equal(4, f.length) + f.freeze + assert_nothing_raised { f.size } + assert_nothing_raised { f.length } end # This test is should in ruby/test_method.rb @@ -822,6 +906,17 @@ class TestStringIO < Test::Unit::TestCase 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 ) @@ -856,6 +951,17 @@ class TestStringIO < Test::Unit::TestCase 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 @@ -899,9 +1005,10 @@ class TestStringIO < Test::Unit::TestCase end def test_overflow - return if RbConfig::SIZEOF["void*"] > RbConfig::SIZEOF["long"] - limit = RbConfig::LIMITS["INTPTR_MAX"] - 0x10 - assert_separately(%w[-rstringio], "#{<<-"begin;"}\n#{<<-"end;"}") + 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 = [] @@ -969,6 +1076,108 @@ class TestStringIO < Test::Unit::TestCase 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 + + def test_eof + f = StringIO.new + assert_equal(true, f.eof) + assert_equal(true, f.eof?) + f.ungetc("1234") + assert_equal(false, f.eof) + assert_equal(false, f.eof?) + f.freeze + assert_nothing_raised { f.eof } + assert_nothing_raised { f.eof? } + end + + def test_pid + f = StringIO.new + assert_equal(nil, f.pid) + f.freeze + assert_nothing_raised { f.pid } + end + + def test_fileno + f = StringIO.new + assert_equal(nil, f.fileno) + f.freeze + assert_nothing_raised { f.fileno } + end + + def test_external_encoding + f = StringIO.new + assert_equal(Encoding.find("external"), f.external_encoding) + f = StringIO.new("1234".encode("UTF-16BE")) + assert_equal(Encoding::UTF_16BE, f.external_encoding) + f.freeze + assert_nothing_raised { f.external_encoding } + end + + def test_string + f = StringIO.new + assert_equal("", f.string) + f = StringIO.new("1234") + assert_equal("1234", f.string) + f.freeze + assert_nothing_raised { f.string } + end + + def test_initialize_copy + f = StringIO.new("1234") + f.read(1) + f2 = f.dup + assert_equal(1, f2.pos) + f.read(1) + assert_equal(2, f2.pos) + f.ungetc("56") + assert_equal(0, f2.pos) + assert_equal("5634", f2.string) + f.freeze + f2 = f.dup + assert_not_predicate(f2, :frozen?) + end + + private + def assert_string(content, encoding, str, mesg = nil) assert_equal([content, encoding], [str, str.encoding], mesg) end |
