1# coding: US-ASCII 2require 'test/unit' 3require 'tmpdir' 4require "fcntl" 5require 'io/nonblock' 6require 'socket' 7require 'stringio' 8require 'timeout' 9require 'tempfile' 10require 'weakref' 11require_relative 'envutil' 12 13class TestIO < Test::Unit::TestCase 14 def have_close_on_exec? 15 begin 16 $stdin.close_on_exec? 17 true 18 rescue NotImplementedError 19 false 20 end 21 end 22 23 def have_nonblock? 24 IO.method_defined?("nonblock=") 25 end 26 27 def pipe(wp, rp) 28 re, we = nil, nil 29 r, w = IO.pipe 30 rt = Thread.new do 31 begin 32 rp.call(r) 33 rescue Exception 34 r.close 35 re = $! 36 end 37 end 38 wt = Thread.new do 39 begin 40 wp.call(w) 41 rescue Exception 42 w.close 43 we = $! 44 end 45 end 46 flunk("timeout") unless wt.join(10) && rt.join(10) 47 ensure 48 w.close unless !w || w.closed? 49 r.close unless !r || r.closed? 50 (wt.kill; wt.join) if wt 51 (rt.kill; rt.join) if rt 52 raise we if we 53 raise re if re 54 end 55 56 def with_pipe 57 r, w = IO.pipe 58 begin 59 yield r, w 60 ensure 61 r.close unless r.closed? 62 w.close unless w.closed? 63 end 64 end 65 66 def with_read_pipe(content) 67 pipe(proc do |w| 68 w << content 69 w.close 70 end, proc do |r| 71 yield r 72 end) 73 end 74 75 def mkcdtmpdir 76 Dir.mktmpdir {|d| 77 Dir.chdir(d) { 78 yield 79 } 80 } 81 end 82 83 def trapping_usr1 84 @usr1_rcvd = 0 85 trap(:USR1) { @usr1_rcvd += 1 } 86 yield 87 ensure 88 trap(:USR1, "DEFAULT") 89 end 90 91 def test_pipe 92 r, w = IO.pipe 93 assert_instance_of(IO, r) 94 assert_instance_of(IO, w) 95 [ 96 Thread.start{ 97 w.print "abc" 98 w.close 99 }, 100 Thread.start{ 101 assert_equal("abc", r.read) 102 r.close 103 } 104 ].each{|thr| thr.join} 105 end 106 107 def test_pipe_block 108 x = nil 109 ret = IO.pipe {|r, w| 110 x = [r,w] 111 assert_instance_of(IO, r) 112 assert_instance_of(IO, w) 113 [ 114 Thread.start do 115 w.print "abc" 116 w.close 117 end, 118 Thread.start do 119 assert_equal("abc", r.read) 120 end 121 ].each{|thr| thr.join} 122 assert(!r.closed?) 123 assert(w.closed?) 124 :foooo 125 } 126 assert_equal(:foooo, ret) 127 assert(x[0].closed?) 128 assert(x[1].closed?) 129 end 130 131 def test_pipe_block_close 132 4.times {|i| 133 x = nil 134 IO.pipe {|r, w| 135 x = [r,w] 136 r.close if (i&1) == 0 137 w.close if (i&2) == 0 138 } 139 assert(x[0].closed?) 140 assert(x[1].closed?) 141 } 142 end 143 144 def test_gets_rs 145 # default_rs 146 pipe(proc do |w| 147 w.print "aaa\nbbb\n" 148 w.close 149 end, proc do |r| 150 assert_equal "aaa\n", r.gets 151 assert_equal "bbb\n", r.gets 152 assert_nil r.gets 153 r.close 154 end) 155 156 # nil 157 pipe(proc do |w| 158 w.print "a\n\nb\n\n" 159 w.close 160 end, proc do |r| 161 assert_equal "a\n\nb\n\n", r.gets(nil) 162 assert_nil r.gets("") 163 r.close 164 end) 165 166 # "\377" 167 pipe(proc do |w| 168 w.print "\377xyz" 169 w.close 170 end, proc do |r| 171 r.binmode 172 assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]") 173 r.close 174 end) 175 176 # "" 177 pipe(proc do |w| 178 w.print "a\n\nb\n\n" 179 w.close 180 end, proc do |r| 181 assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]" 182 assert_equal "b\n\n", r.gets("") 183 assert_nil r.gets("") 184 r.close 185 end) 186 end 187 188 def test_gets_limit_extra_arg 189 pipe(proc do |w| 190 w << "0123456789\n0123456789" 191 w.close 192 end, proc do |r| 193 assert_equal("0123456789\n0", r.gets(nil, 12)) 194 assert_raise(TypeError) { r.gets(3,nil) } 195 end) 196 end 197 198 # This test cause SEGV. 199 def test_ungetc 200 pipe(proc do |w| 201 w.close 202 end, proc do |r| 203 s = "a" * 1000 204 assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } } 205 end) 206 end 207 208 def test_ungetbyte 209 make_tempfile {|t| 210 t.open 211 t.binmode 212 t.ungetbyte(0x41) 213 assert_equal(-1, t.pos) 214 assert_equal(0x41, t.getbyte) 215 t.rewind 216 assert_equal(0, t.pos) 217 t.ungetbyte("qux") 218 assert_equal(-3, t.pos) 219 assert_equal("quxfoo\n", t.gets) 220 assert_equal(4, t.pos) 221 t.set_encoding("utf-8") 222 t.ungetbyte(0x89) 223 t.ungetbyte(0x8e) 224 t.ungetbyte("\xe7") 225 t.ungetbyte("\xe7\xb4\x85") 226 assert_equal(-2, t.pos) 227 assert_equal("\u7d05\u7389bar\n", t.gets) 228 } 229 end 230 231 def test_each_byte 232 pipe(proc do |w| 233 w << "abc def" 234 w.close 235 end, proc do |r| 236 r.each_byte {|byte| break if byte == 32 } 237 assert_equal("def", r.read, "[ruby-dev:31659]") 238 end) 239 end 240 241 def test_each_byte_with_seek 242 make_tempfile {|t| 243 bug5119 = '[ruby-core:38609]' 244 i = 0 245 open(t.path) do |f| 246 f.each_byte {i = f.pos} 247 end 248 assert_equal(12, i, bug5119) 249 } 250 end 251 252 def test_each_codepoint 253 make_tempfile {|t| 254 bug2959 = '[ruby-core:28650]' 255 a = "" 256 File.open(t, 'rt') {|f| 257 f.each_codepoint {|c| a << c} 258 } 259 assert_equal("foo\nbar\nbaz\n", a, bug2959) 260 } 261 end 262 263 def test_codepoints 264 make_tempfile {|t| 265 bug2959 = '[ruby-core:28650]' 266 a = "" 267 File.open(t, 'rt') {|f| 268 assert_warn(/deprecated/) { 269 f.codepoints {|c| a << c} 270 } 271 } 272 assert_equal("foo\nbar\nbaz\n", a, bug2959) 273 } 274 end 275 276 def test_rubydev33072 277 t = make_tempfile 278 path = t.path 279 t.close! 280 assert_raise(Errno::ENOENT, "[ruby-dev:33072]") do 281 File.read(path, nil, nil, {}) 282 end 283 end 284 285 def test_copy_stream 286 mkcdtmpdir { 287 288 content = "foobar" 289 File.open("src", "w") {|f| f << content } 290 ret = IO.copy_stream("src", "dst") 291 assert_equal(content.bytesize, ret) 292 assert_equal(content, File.read("dst")) 293 294 # overwrite by smaller file. 295 content = "baz" 296 File.open("src", "w") {|f| f << content } 297 ret = IO.copy_stream("src", "dst") 298 assert_equal(content.bytesize, ret) 299 assert_equal(content, File.read("dst")) 300 301 ret = IO.copy_stream("src", "dst", 2) 302 assert_equal(2, ret) 303 assert_equal(content[0,2], File.read("dst")) 304 305 ret = IO.copy_stream("src", "dst", 0) 306 assert_equal(0, ret) 307 assert_equal("", File.read("dst")) 308 309 ret = IO.copy_stream("src", "dst", nil, 1) 310 assert_equal(content.bytesize-1, ret) 311 assert_equal(content[1..-1], File.read("dst")) 312 313 assert_raise(Errno::ENOENT) { 314 IO.copy_stream("nodir/foo", "dst") 315 } 316 317 assert_raise(Errno::ENOENT) { 318 IO.copy_stream("src", "nodir/bar") 319 } 320 321 pipe(proc do |w| 322 ret = IO.copy_stream("src", w) 323 assert_equal(content.bytesize, ret) 324 w.close 325 end, proc do |r| 326 assert_equal(content, r.read) 327 end) 328 329 with_pipe {|r, w| 330 w.close 331 assert_raise(IOError) { IO.copy_stream("src", w) } 332 } 333 334 pipe_content = "abc" 335 with_read_pipe(pipe_content) {|r| 336 ret = IO.copy_stream(r, "dst") 337 assert_equal(pipe_content.bytesize, ret) 338 assert_equal(pipe_content, File.read("dst")) 339 } 340 341 with_read_pipe("abc") {|r1| 342 assert_equal("a", r1.getc) 343 pipe(proc do |w2| 344 w2.sync = false 345 w2 << "def" 346 ret = IO.copy_stream(r1, w2) 347 assert_equal(2, ret) 348 w2.close 349 end, proc do |r2| 350 assert_equal("defbc", r2.read) 351 end) 352 } 353 354 with_read_pipe("abc") {|r1| 355 assert_equal("a", r1.getc) 356 pipe(proc do |w2| 357 w2.sync = false 358 w2 << "def" 359 ret = IO.copy_stream(r1, w2, 1) 360 assert_equal(1, ret) 361 w2.close 362 end, proc do |r2| 363 assert_equal("defb", r2.read) 364 end) 365 } 366 367 with_read_pipe("abc") {|r1| 368 assert_equal("a", r1.getc) 369 pipe(proc do |w2| 370 ret = IO.copy_stream(r1, w2) 371 assert_equal(2, ret) 372 w2.close 373 end, proc do |r2| 374 assert_equal("bc", r2.read) 375 end) 376 } 377 378 with_read_pipe("abc") {|r1| 379 assert_equal("a", r1.getc) 380 pipe(proc do |w2| 381 ret = IO.copy_stream(r1, w2, 1) 382 assert_equal(1, ret) 383 w2.close 384 end, proc do |r2| 385 assert_equal("b", r2.read) 386 end) 387 } 388 389 with_read_pipe("abc") {|r1| 390 assert_equal("a", r1.getc) 391 pipe(proc do |w2| 392 ret = IO.copy_stream(r1, w2, 0) 393 assert_equal(0, ret) 394 w2.close 395 end, proc do |r2| 396 assert_equal("", r2.read) 397 end) 398 } 399 400 pipe(proc do |w1| 401 w1 << "abc" 402 w1 << "def" 403 w1.close 404 end, proc do |r1| 405 assert_equal("a", r1.getc) 406 pipe(proc do |w2| 407 ret = IO.copy_stream(r1, w2) 408 assert_equal(5, ret) 409 w2.close 410 end, proc do |r2| 411 assert_equal("bcdef", r2.read) 412 end) 413 end) 414 415 pipe(proc do |w| 416 ret = IO.copy_stream("src", w, 1, 1) 417 assert_equal(1, ret) 418 w.close 419 end, proc do |r| 420 assert_equal(content[1,1], r.read) 421 end) 422 423 if have_nonblock? 424 with_read_pipe("abc") {|r1| 425 assert_equal("a", r1.getc) 426 with_pipe {|r2, w2| 427 begin 428 w2.nonblock = true 429 rescue Errno::EBADF 430 skip "nonblocking IO for pipe is not implemented" 431 break 432 end 433 s = w2.syswrite("a" * 100000) 434 t = Thread.new { sleep 0.1; r2.read } 435 ret = IO.copy_stream(r1, w2) 436 w2.close 437 assert_equal(2, ret) 438 assert_equal("a" * s + "bc", t.value) 439 } 440 } 441 end 442 443 bigcontent = "abc" * 123456 444 File.open("bigsrc", "w") {|f| f << bigcontent } 445 ret = IO.copy_stream("bigsrc", "bigdst") 446 assert_equal(bigcontent.bytesize, ret) 447 assert_equal(bigcontent, File.read("bigdst")) 448 449 File.unlink("bigdst") 450 ret = IO.copy_stream("bigsrc", "bigdst", nil, 100) 451 assert_equal(bigcontent.bytesize-100, ret) 452 assert_equal(bigcontent[100..-1], File.read("bigdst")) 453 454 File.unlink("bigdst") 455 ret = IO.copy_stream("bigsrc", "bigdst", 30000, 100) 456 assert_equal(30000, ret) 457 assert_equal(bigcontent[100, 30000], File.read("bigdst")) 458 459 File.open("bigsrc") {|f| 460 begin 461 assert_equal(0, f.pos) 462 ret = IO.copy_stream(f, "bigdst", nil, 10) 463 assert_equal(bigcontent.bytesize-10, ret) 464 assert_equal(bigcontent[10..-1], File.read("bigdst")) 465 assert_equal(0, f.pos) 466 ret = IO.copy_stream(f, "bigdst", 40, 30) 467 assert_equal(40, ret) 468 assert_equal(bigcontent[30, 40], File.read("bigdst")) 469 assert_equal(0, f.pos) 470 rescue NotImplementedError 471 #skip "pread(2) is not implemtented." 472 end 473 } 474 475 with_pipe {|r, w| 476 w.close 477 assert_raise(IOError) { IO.copy_stream("src", w) } 478 } 479 480 megacontent = "abc" * 1234567 481 File.open("megasrc", "w") {|f| f << megacontent } 482 483 if have_nonblock? 484 with_pipe {|r1, w1| 485 with_pipe {|r2, w2| 486 begin 487 r1.nonblock = true 488 w2.nonblock = true 489 rescue Errno::EBADF 490 skip "nonblocking IO for pipe is not implemented" 491 end 492 t1 = Thread.new { w1 << megacontent; w1.close } 493 t2 = Thread.new { r2.read } 494 ret = IO.copy_stream(r1, w2) 495 assert_equal(megacontent.bytesize, ret) 496 w2.close 497 t1.join 498 assert_equal(megacontent, t2.value) 499 } 500 } 501 end 502 503 with_pipe {|r1, w1| 504 with_pipe {|r2, w2| 505 t1 = Thread.new { w1 << megacontent; w1.close } 506 t2 = Thread.new { r2.read } 507 ret = IO.copy_stream(r1, w2) 508 assert_equal(megacontent.bytesize, ret) 509 w2.close 510 t1.join 511 assert_equal(megacontent, t2.value) 512 } 513 } 514 515 with_pipe {|r, w| 516 t = Thread.new { r.read } 517 ret = IO.copy_stream("megasrc", w) 518 assert_equal(megacontent.bytesize, ret) 519 w.close 520 assert_equal(megacontent, t.value) 521 } 522 } 523 end 524 525 def test_copy_stream_rbuf 526 mkcdtmpdir { 527 begin 528 pipe(proc do |w| 529 File.open("foo", "w") {|f| f << "abcd" } 530 File.open("foo") {|f| 531 f.read(1) 532 assert_equal(3, IO.copy_stream(f, w, 10, 1)) 533 } 534 w.close 535 end, proc do |r| 536 assert_equal("bcd", r.read) 537 end) 538 rescue NotImplementedError 539 skip "pread(2) is not implemtented." 540 end 541 } 542 end 543 544 def with_socketpair 545 s1, s2 = UNIXSocket.pair 546 begin 547 yield s1, s2 548 ensure 549 s1.close unless s1.closed? 550 s2.close unless s2.closed? 551 end 552 end 553 554 def test_copy_stream_socket1 555 mkcdtmpdir { 556 content = "foobar" 557 File.open("src", "w") {|f| f << content } 558 559 with_socketpair {|s1, s2| 560 ret = IO.copy_stream("src", s1) 561 assert_equal(content.bytesize, ret) 562 s1.close 563 assert_equal(content, s2.read) 564 } 565 } 566 end if defined? UNIXSocket 567 568 def test_copy_stream_socket2 569 mkcdtmpdir { 570 bigcontent = "abc" * 123456 571 File.open("bigsrc", "w") {|f| f << bigcontent } 572 573 with_socketpair {|s1, s2| 574 t = Thread.new { s2.read } 575 ret = IO.copy_stream("bigsrc", s1) 576 assert_equal(bigcontent.bytesize, ret) 577 s1.close 578 result = t.value 579 assert_equal(bigcontent, result) 580 } 581 } 582 end if defined? UNIXSocket 583 584 def test_copy_stream_socket3 585 mkcdtmpdir { 586 bigcontent = "abc" * 123456 587 File.open("bigsrc", "w") {|f| f << bigcontent } 588 589 with_socketpair {|s1, s2| 590 t = Thread.new { s2.read } 591 ret = IO.copy_stream("bigsrc", s1, 10000) 592 assert_equal(10000, ret) 593 s1.close 594 result = t.value 595 assert_equal(bigcontent[0,10000], result) 596 } 597 } 598 end if defined? UNIXSocket 599 600 def test_copy_stream_socket4 601 mkcdtmpdir { 602 bigcontent = "abc" * 123456 603 File.open("bigsrc", "w") {|f| f << bigcontent } 604 605 File.open("bigsrc") {|f| 606 assert_equal(0, f.pos) 607 with_socketpair {|s1, s2| 608 t = Thread.new { s2.read } 609 ret = IO.copy_stream(f, s1, nil, 100) 610 assert_equal(bigcontent.bytesize-100, ret) 611 assert_equal(0, f.pos) 612 s1.close 613 result = t.value 614 assert_equal(bigcontent[100..-1], result) 615 } 616 } 617 } 618 end if defined? UNIXSocket 619 620 def test_copy_stream_socket5 621 mkcdtmpdir { 622 bigcontent = "abc" * 123456 623 File.open("bigsrc", "w") {|f| f << bigcontent } 624 625 File.open("bigsrc") {|f| 626 assert_equal(bigcontent[0,100], f.read(100)) 627 assert_equal(100, f.pos) 628 with_socketpair {|s1, s2| 629 t = Thread.new { s2.read } 630 ret = IO.copy_stream(f, s1) 631 assert_equal(bigcontent.bytesize-100, ret) 632 assert_equal(bigcontent.length, f.pos) 633 s1.close 634 result = t.value 635 assert_equal(bigcontent[100..-1], result) 636 } 637 } 638 } 639 end if defined? UNIXSocket 640 641 def test_copy_stream_socket6 642 mkcdtmpdir { 643 megacontent = "abc" * 1234567 644 File.open("megasrc", "w") {|f| f << megacontent } 645 646 with_socketpair {|s1, s2| 647 begin 648 s1.nonblock = true 649 rescue Errno::EBADF 650 skip "nonblocking IO for pipe is not implemented" 651 end 652 t = Thread.new { s2.read } 653 ret = IO.copy_stream("megasrc", s1) 654 assert_equal(megacontent.bytesize, ret) 655 s1.close 656 result = t.value 657 assert_equal(megacontent, result) 658 } 659 } 660 end if defined? UNIXSocket 661 662 def test_copy_stream_socket7 663 mkcdtmpdir { 664 megacontent = "abc" * 1234567 665 File.open("megasrc", "w") {|f| f << megacontent } 666 667 with_socketpair {|s1, s2| 668 begin 669 s1.nonblock = true 670 rescue Errno::EBADF 671 skip "nonblocking IO for pipe is not implemented" 672 end 673 trapping_usr1 do 674 nr = 30 675 begin 676 pid = fork do 677 s1.close 678 IO.select([s2]) 679 Process.kill(:USR1, Process.ppid) 680 s2.read 681 end 682 s2.close 683 nr.times do 684 assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1) 685 end 686 assert_equal(1, @usr1_rcvd) 687 ensure 688 s1.close 689 _, status = Process.waitpid2(pid) if pid 690 end 691 assert status.success?, status.inspect 692 end 693 } 694 } 695 end if defined? UNIXSocket and IO.method_defined?("nonblock=") 696 697 def test_copy_stream_strio 698 src = StringIO.new("abcd") 699 dst = StringIO.new 700 ret = IO.copy_stream(src, dst) 701 assert_equal(4, ret) 702 assert_equal("abcd", dst.string) 703 assert_equal(4, src.pos) 704 end 705 706 def test_copy_stream_strio_len 707 src = StringIO.new("abcd") 708 dst = StringIO.new 709 ret = IO.copy_stream(src, dst, 3) 710 assert_equal(3, ret) 711 assert_equal("abc", dst.string) 712 assert_equal(3, src.pos) 713 end 714 715 def test_copy_stream_strio_off 716 src = StringIO.new("abcd") 717 with_pipe {|r, w| 718 assert_raise(ArgumentError) { 719 IO.copy_stream(src, w, 3, 1) 720 } 721 } 722 end 723 724 def test_copy_stream_fname_to_strio 725 mkcdtmpdir { 726 File.open("foo", "w") {|f| f << "abcd" } 727 src = "foo" 728 dst = StringIO.new 729 ret = IO.copy_stream(src, dst, 3) 730 assert_equal(3, ret) 731 assert_equal("abc", dst.string) 732 } 733 end 734 735 def test_copy_stream_strio_to_fname 736 mkcdtmpdir { 737 # StringIO to filename 738 src = StringIO.new("abcd") 739 ret = IO.copy_stream(src, "fooo", 3) 740 assert_equal(3, ret) 741 assert_equal("abc", File.read("fooo")) 742 assert_equal(3, src.pos) 743 } 744 end 745 746 def test_copy_stream_io_to_strio 747 mkcdtmpdir { 748 # IO to StringIO 749 File.open("bar", "w") {|f| f << "abcd" } 750 File.open("bar") {|src| 751 dst = StringIO.new 752 ret = IO.copy_stream(src, dst, 3) 753 assert_equal(3, ret) 754 assert_equal("abc", dst.string) 755 assert_equal(3, src.pos) 756 } 757 } 758 end 759 760 def test_copy_stream_strio_to_io 761 mkcdtmpdir { 762 # StringIO to IO 763 src = StringIO.new("abcd") 764 ret = File.open("baz", "w") {|dst| 765 IO.copy_stream(src, dst, 3) 766 } 767 assert_equal(3, ret) 768 assert_equal("abc", File.read("baz")) 769 assert_equal(3, src.pos) 770 } 771 end 772 773 class Rot13IO 774 def initialize(io) 775 @io = io 776 end 777 778 def readpartial(*args) 779 ret = @io.readpartial(*args) 780 ret.tr!('a-zA-Z', 'n-za-mN-ZA-M') 781 ret 782 end 783 784 def write(str) 785 @io.write(str.tr('a-zA-Z', 'n-za-mN-ZA-M')) 786 end 787 788 def to_io 789 @io 790 end 791 end 792 793 def test_copy_stream_io_to_rot13 794 mkcdtmpdir { 795 File.open("bar", "w") {|f| f << "vex" } 796 File.open("bar") {|src| 797 File.open("baz", "w") {|dst0| 798 dst = Rot13IO.new(dst0) 799 ret = IO.copy_stream(src, dst, 3) 800 assert_equal(3, ret) 801 } 802 assert_equal("irk", File.read("baz")) 803 } 804 } 805 end 806 807 def test_copy_stream_rot13_to_io 808 mkcdtmpdir { 809 File.open("bar", "w") {|f| f << "flap" } 810 File.open("bar") {|src0| 811 src = Rot13IO.new(src0) 812 File.open("baz", "w") {|dst| 813 ret = IO.copy_stream(src, dst, 4) 814 assert_equal(4, ret) 815 } 816 } 817 assert_equal("sync", File.read("baz")) 818 } 819 end 820 821 def test_copy_stream_rot13_to_rot13 822 mkcdtmpdir { 823 File.open("bar", "w") {|f| f << "bin" } 824 File.open("bar") {|src0| 825 src = Rot13IO.new(src0) 826 File.open("baz", "w") {|dst0| 827 dst = Rot13IO.new(dst0) 828 ret = IO.copy_stream(src, dst, 3) 829 assert_equal(3, ret) 830 } 831 } 832 assert_equal("bin", File.read("baz")) 833 } 834 end 835 836 def test_copy_stream_strio_flush 837 with_pipe {|r, w| 838 w.sync = false 839 w.write "zz" 840 src = StringIO.new("abcd") 841 IO.copy_stream(src, w) 842 t = Thread.new { 843 w.close 844 } 845 assert_equal("zzabcd", r.read) 846 t.join 847 } 848 end 849 850 def test_copy_stream_strio_rbuf 851 pipe(proc do |w| 852 w << "abcd" 853 w.close 854 end, proc do |r| 855 assert_equal("a", r.read(1)) 856 sio = StringIO.new 857 IO.copy_stream(r, sio) 858 assert_equal("bcd", sio.string) 859 end) 860 end 861 862 def test_copy_stream_src_wbuf 863 mkcdtmpdir { 864 pipe(proc do |w| 865 File.open("foe", "w+") {|f| 866 f.write "abcd\n" 867 f.rewind 868 f.write "xy" 869 IO.copy_stream(f, w) 870 } 871 assert_equal("xycd\n", File.read("foe")) 872 w.close 873 end, proc do |r| 874 assert_equal("cd\n", r.read) 875 r.close 876 end) 877 } 878 end 879 880 class Bug5237 881 attr_reader :count 882 def initialize 883 @count = 0 884 end 885 886 def read(bytes, buffer) 887 @count += 1 888 buffer.replace "this is a test" 889 nil 890 end 891 end 892 893 def test_copy_stream_broken_src_read_eof 894 src = Bug5237.new 895 dst = StringIO.new 896 assert_equal 0, src.count 897 th = Thread.new { IO.copy_stream(src, dst) } 898 flunk("timeout") unless th.join(10) 899 assert_equal 1, src.count 900 end 901 902 def test_copy_stream_dst_rbuf 903 mkcdtmpdir { 904 pipe(proc do |w| 905 w << "xyz" 906 w.close 907 end, proc do |r| 908 File.open("fom", "w+b") {|f| 909 f.write "abcd\n" 910 f.rewind 911 assert_equal("abc", f.read(3)) 912 f.ungetc "c" 913 IO.copy_stream(r, f) 914 } 915 assert_equal("abxyz", File.read("fom")) 916 end) 917 } 918 end 919 920 def safe_4 921 t = Thread.new do 922 $SAFE = 4 923 yield 924 end 925 unless t.join(10) 926 t.kill 927 flunk("timeout in safe_4") 928 end 929 end 930 931 def ruby(*args) 932 args = ['-e', '$>.write($<.read)'] if args.empty? 933 ruby = EnvUtil.rubybin 934 f = IO.popen([ruby] + args, 'r+') 935 yield(f) 936 ensure 937 f.close unless !f || f.closed? 938 end 939 940 def test_try_convert 941 assert_equal(STDOUT, IO.try_convert(STDOUT)) 942 assert_equal(nil, IO.try_convert("STDOUT")) 943 end 944 945 def test_ungetc2 946 f = false 947 pipe(proc do |w| 948 Thread.pass until f 949 w.write("1" * 10000) 950 w.close 951 end, proc do |r| 952 r.ungetc("0" * 10000) 953 f = true 954 assert_equal("0" * 10000 + "1" * 10000, r.read) 955 end) 956 end 957 958 def test_write_non_writable 959 with_pipe do |r, w| 960 assert_raise(IOError) do 961 r.write "foobarbaz" 962 end 963 end 964 end 965 966 def test_dup 967 ruby do |f| 968 f2 = f.dup 969 f.puts "foo" 970 f2.puts "bar" 971 f.close_write 972 f2.close_write 973 assert_equal("foo\nbar\n", f.read) 974 assert_equal("", f2.read) 975 end 976 end 977 978 def test_dup_many 979 ruby('-e', <<-'End') {|f| 980 ok = 0 981 a = [] 982 begin 983 loop {a << IO.pipe} 984 rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM 985 ok += 1 986 end 987 print "no" if ok != 1 988 begin 989 loop {a << [a[-1][0].dup, a[-1][1].dup]} 990 rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM 991 ok += 1 992 end 993 print "no" if ok != 2 994 print "ok" 995 End 996 assert_equal("ok", f.read) 997 } 998 end 999 1000 def test_inspect 1001 with_pipe do |r, w| 1002 assert_match(/^#<IO:fd \d+>$/, r.inspect) 1003 assert_raise(SecurityError) do 1004 safe_4 { r.inspect } 1005 end 1006 end 1007 end 1008 1009 def test_readpartial 1010 pipe(proc do |w| 1011 w.write "foobarbaz" 1012 w.close 1013 end, proc do |r| 1014 assert_raise(ArgumentError) { r.readpartial(-1) } 1015 assert_equal("fooba", r.readpartial(5)) 1016 r.readpartial(5, s = "") 1017 assert_equal("rbaz", s) 1018 end) 1019 end 1020 1021 def test_readpartial_lock 1022 with_pipe do |r, w| 1023 s = "" 1024 t = Thread.new { r.readpartial(5, s) } 1025 Thread.pass until t.stop? 1026 assert_raise(RuntimeError) { s.clear } 1027 w.write "foobarbaz" 1028 w.close 1029 assert_equal("fooba", t.value) 1030 end 1031 end 1032 1033 def test_readpartial_pos 1034 mkcdtmpdir { 1035 open("foo", "w") {|f| f << "abc" } 1036 open("foo") {|f| 1037 f.seek(0) 1038 assert_equal("ab", f.readpartial(2)) 1039 assert_equal(2, f.pos) 1040 } 1041 } 1042 end 1043 1044 def test_readpartial_with_not_empty_buffer 1045 pipe(proc do |w| 1046 w.write "foob" 1047 w.close 1048 end, proc do |r| 1049 r.readpartial(5, s = "01234567") 1050 assert_equal("foob", s) 1051 end) 1052 end 1053 1054 def test_readpartial_buffer_error 1055 with_pipe do |r, w| 1056 s = "" 1057 t = Thread.new { r.readpartial(5, s) } 1058 Thread.pass until t.stop? 1059 t.kill 1060 t.value 1061 assert_equal("", s) 1062 end 1063 end 1064 1065 def test_read 1066 pipe(proc do |w| 1067 w.write "foobarbaz" 1068 w.close 1069 end, proc do |r| 1070 assert_raise(ArgumentError) { r.read(-1) } 1071 assert_equal("fooba", r.read(5)) 1072 r.read(nil, s = "") 1073 assert_equal("rbaz", s) 1074 end) 1075 end 1076 1077 def test_read_lock 1078 with_pipe do |r, w| 1079 s = "" 1080 t = Thread.new { r.read(5, s) } 1081 Thread.pass until t.stop? 1082 assert_raise(RuntimeError) { s.clear } 1083 w.write "foobarbaz" 1084 w.close 1085 assert_equal("fooba", t.value) 1086 end 1087 end 1088 1089 def test_read_with_not_empty_buffer 1090 pipe(proc do |w| 1091 w.write "foob" 1092 w.close 1093 end, proc do |r| 1094 r.read(nil, s = "01234567") 1095 assert_equal("foob", s) 1096 end) 1097 end 1098 1099 def test_read_buffer_error 1100 with_pipe do |r, w| 1101 s = "" 1102 t = Thread.new { r.read(5, s) } 1103 Thread.pass until t.stop? 1104 t.kill 1105 t.value 1106 assert_equal("", s) 1107 end 1108 end 1109 1110 def test_write_nonblock 1111 skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 1112 pipe(proc do |w| 1113 w.write_nonblock(1) 1114 w.close 1115 end, proc do |r| 1116 assert_equal("1", r.read) 1117 end) 1118 end 1119 1120 def test_read_nonblock_with_not_empty_buffer 1121 skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 1122 with_pipe {|r, w| 1123 w.write "foob" 1124 w.close 1125 r.read_nonblock(5, s = "01234567") 1126 assert_equal("foob", s) 1127 } 1128 end 1129 1130 def test_read_nonblock_error 1131 return if !have_nonblock? 1132 skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 1133 with_pipe {|r, w| 1134 begin 1135 r.read_nonblock 4096 1136 rescue Errno::EWOULDBLOCK 1137 assert_kind_of(IO::WaitReadable, $!) 1138 end 1139 } 1140 end 1141 1142 def test_write_nonblock_error 1143 return if !have_nonblock? 1144 skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 1145 with_pipe {|r, w| 1146 begin 1147 loop { 1148 w.write_nonblock "a"*100000 1149 } 1150 rescue Errno::EWOULDBLOCK 1151 assert_kind_of(IO::WaitWritable, $!) 1152 end 1153 } 1154 end 1155 1156 def test_gets 1157 pipe(proc do |w| 1158 w.write "foobarbaz" 1159 w.close 1160 end, proc do |r| 1161 assert_equal("", r.gets(0)) 1162 assert_equal("foobarbaz", r.gets(9)) 1163 end) 1164 end 1165 1166 def test_close_read 1167 ruby do |f| 1168 f.close_read 1169 f.write "foobarbaz" 1170 assert_raise(IOError) { f.read } 1171 end 1172 end 1173 1174 def test_close_read_pipe 1175 with_pipe do |r, w| 1176 r.close_read 1177 assert_raise(Errno::EPIPE) { w.write "foobarbaz" } 1178 end 1179 end 1180 1181 def test_close_read_security_error 1182 with_pipe do |r, w| 1183 assert_raise(SecurityError) do 1184 safe_4 { r.close_read } 1185 end 1186 end 1187 end 1188 1189 def test_close_read_non_readable 1190 with_pipe do |r, w| 1191 assert_raise(IOError) do 1192 w.close_read 1193 end 1194 end 1195 end 1196 1197 def test_close_write 1198 ruby do |f| 1199 f.write "foobarbaz" 1200 f.close_write 1201 assert_equal("foobarbaz", f.read) 1202 end 1203 end 1204 1205 def test_close_write_security_error 1206 with_pipe do |r, w| 1207 assert_raise(SecurityError) do 1208 safe_4 { r.close_write } 1209 end 1210 end 1211 end 1212 1213 def test_close_write_non_readable 1214 with_pipe do |r, w| 1215 assert_raise(IOError) do 1216 r.close_write 1217 end 1218 end 1219 end 1220 1221 def test_close_read_write_separately 1222 bug = '[ruby-list:49598]' 1223 (1..10).each do |i| 1224 assert_nothing_raised(IOError, "#{bug} trying ##{i}") do 1225 IO.popen(EnvUtil.rubybin, "r+") {|f| 1226 th = Thread.new {f.close_write} 1227 f.close_read 1228 th.join 1229 } 1230 end 1231 end 1232 end 1233 1234 def test_pid 1235 r, w = IO.pipe 1236 assert_equal(nil, r.pid) 1237 assert_equal(nil, w.pid) 1238 1239 pipe = IO.popen(EnvUtil.rubybin, "r+") 1240 pid1 = pipe.pid 1241 pipe.puts "p $$" 1242 pipe.close_write 1243 pid2 = pipe.read.chomp.to_i 1244 assert_equal(pid2, pid1) 1245 assert_equal(pid2, pipe.pid) 1246 pipe.close 1247 assert_raise(IOError) { pipe.pid } 1248 end 1249 1250 def tesst_pid_after_close_read 1251 pid1 = pid2 = nil 1252 IO.popen(["echo", ""], "r+") do |io| 1253 pid1 = io.pid 1254 io.close_read 1255 pid2 = io.pid 1256 end 1257 assert_not_nil(pid1) 1258 assert_equal(pid1, pid2) 1259 end 1260 1261 def make_tempfile 1262 t = Tempfile.new("test_io") 1263 t.binmode 1264 t.puts "foo" 1265 t.puts "bar" 1266 t.puts "baz" 1267 t.close 1268 if block_given? 1269 begin 1270 yield t 1271 ensure 1272 t.close(true) 1273 end 1274 else 1275 t 1276 end 1277 end 1278 1279 def test_set_lineno 1280 make_tempfile {|t| 1281 ruby("-e", <<-SRC, t.path) do |f| 1282 open(ARGV[0]) do |f| 1283 p $. 1284 f.gets; p $. 1285 f.gets; p $. 1286 f.lineno = 1000; p $. 1287 f.gets; p $. 1288 f.gets; p $. 1289 f.rewind; p $. 1290 f.gets; p $. 1291 f.gets; p $. 1292 f.gets; p $. 1293 f.gets; p $. 1294 end 1295 SRC 1296 assert_equal("0,1,2,2,1001,1001,1001,1,2,3,3", f.read.chomp.gsub("\n", ",")) 1297 end 1298 1299 pipe(proc do |w| 1300 w.puts "foo" 1301 w.puts "bar" 1302 w.puts "baz" 1303 w.close 1304 end, proc do |r| 1305 r.gets; assert_equal(1, $.) 1306 r.gets; assert_equal(2, $.) 1307 r.lineno = 1000; assert_equal(2, $.) 1308 r.gets; assert_equal(1001, $.) 1309 r.gets; assert_equal(1001, $.) 1310 end) 1311 } 1312 end 1313 1314 def test_readline 1315 pipe(proc do |w| 1316 w.puts "foo" 1317 w.puts "bar" 1318 w.puts "baz" 1319 w.close 1320 end, proc do |r| 1321 r.readline; assert_equal(1, $.) 1322 r.readline; assert_equal(2, $.) 1323 r.lineno = 1000; assert_equal(2, $.) 1324 r.readline; assert_equal(1001, $.) 1325 assert_raise(EOFError) { r.readline } 1326 end) 1327 end 1328 1329 def test_each_char 1330 pipe(proc do |w| 1331 w.puts "foo" 1332 w.puts "bar" 1333 w.puts "baz" 1334 w.close 1335 end, proc do |r| 1336 a = [] 1337 r.each_char {|c| a << c } 1338 assert_equal(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"], a) 1339 end) 1340 end 1341 1342 def test_lines 1343 verbose, $VERBOSE = $VERBOSE, nil 1344 pipe(proc do |w| 1345 w.puts "foo" 1346 w.puts "bar" 1347 w.puts "baz" 1348 w.close 1349 end, proc do |r| 1350 e = nil 1351 assert_warn(/deprecated/) { 1352 e = r.lines 1353 } 1354 assert_equal("foo\n", e.next) 1355 assert_equal("bar\n", e.next) 1356 assert_equal("baz\n", e.next) 1357 assert_raise(StopIteration) { e.next } 1358 end) 1359 ensure 1360 $VERBOSE = verbose 1361 end 1362 1363 def test_bytes 1364 verbose, $VERBOSE = $VERBOSE, nil 1365 pipe(proc do |w| 1366 w.binmode 1367 w.puts "foo" 1368 w.puts "bar" 1369 w.puts "baz" 1370 w.close 1371 end, proc do |r| 1372 e = nil 1373 assert_warn(/deprecated/) { 1374 e = r.bytes 1375 } 1376 (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c| 1377 assert_equal(c.ord, e.next) 1378 end 1379 assert_raise(StopIteration) { e.next } 1380 end) 1381 ensure 1382 $VERBOSE = verbose 1383 end 1384 1385 def test_chars 1386 verbose, $VERBOSE = $VERBOSE, nil 1387 pipe(proc do |w| 1388 w.puts "foo" 1389 w.puts "bar" 1390 w.puts "baz" 1391 w.close 1392 end, proc do |r| 1393 e = nil 1394 assert_warn(/deprecated/) { 1395 e = r.chars 1396 } 1397 (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c| 1398 assert_equal(c, e.next) 1399 end 1400 assert_raise(StopIteration) { e.next } 1401 end) 1402 ensure 1403 $VERBOSE = verbose 1404 end 1405 1406 def test_readbyte 1407 pipe(proc do |w| 1408 w.binmode 1409 w.puts "foo" 1410 w.puts "bar" 1411 w.puts "baz" 1412 w.close 1413 end, proc do |r| 1414 r.binmode 1415 (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c| 1416 assert_equal(c.ord, r.readbyte) 1417 end 1418 assert_raise(EOFError) { r.readbyte } 1419 end) 1420 end 1421 1422 def test_readchar 1423 pipe(proc do |w| 1424 w.puts "foo" 1425 w.puts "bar" 1426 w.puts "baz" 1427 w.close 1428 end, proc do |r| 1429 (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c| 1430 assert_equal(c, r.readchar) 1431 end 1432 assert_raise(EOFError) { r.readchar } 1433 end) 1434 end 1435 1436 def test_close_on_exec 1437 skip "IO\#close_on_exec is not implemented." unless have_close_on_exec? 1438 ruby do |f| 1439 assert_equal(true, f.close_on_exec?) 1440 f.close_on_exec = false 1441 assert_equal(false, f.close_on_exec?) 1442 f.close_on_exec = true 1443 assert_equal(true, f.close_on_exec?) 1444 f.close_on_exec = false 1445 assert_equal(false, f.close_on_exec?) 1446 end 1447 1448 with_pipe do |r, w| 1449 assert_equal(true, r.close_on_exec?) 1450 r.close_on_exec = false 1451 assert_equal(false, r.close_on_exec?) 1452 r.close_on_exec = true 1453 assert_equal(true, r.close_on_exec?) 1454 r.close_on_exec = false 1455 assert_equal(false, r.close_on_exec?) 1456 1457 assert_equal(true, w.close_on_exec?) 1458 w.close_on_exec = false 1459 assert_equal(false, w.close_on_exec?) 1460 w.close_on_exec = true 1461 assert_equal(true, w.close_on_exec?) 1462 w.close_on_exec = false 1463 assert_equal(false, w.close_on_exec?) 1464 end 1465 end 1466 1467 def test_close_security_error 1468 with_pipe do |r, w| 1469 assert_raise(SecurityError) do 1470 safe_4 { r.close } 1471 end 1472 end 1473 end 1474 1475 def test_pos 1476 make_tempfile {|t| 1477 1478 open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f| 1479 f.write "Hello" 1480 assert_equal(5, f.pos) 1481 end 1482 open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f| 1483 f.sync = true 1484 f.read 1485 f.write "Hello" 1486 assert_equal(5, f.pos) 1487 end 1488 } 1489 end 1490 1491 def test_pos_with_getc 1492 bug6179 = '[ruby-core:43497]' 1493 make_tempfile {|t| 1494 ["", "t", "b"].each do |mode| 1495 open(t.path, "w#{mode}") do |f| 1496 f.write "0123456789\n" 1497 end 1498 1499 open(t.path, "r#{mode}") do |f| 1500 assert_equal 0, f.pos, "mode=r#{mode}" 1501 assert_equal '0', f.getc, "mode=r#{mode}" 1502 assert_equal 1, f.pos, "mode=r#{mode}" 1503 assert_equal '1', f.getc, "mode=r#{mode}" 1504 assert_equal 2, f.pos, "mode=r#{mode}" 1505 assert_equal '2', f.getc, "mode=r#{mode}" 1506 assert_equal 3, f.pos, "mode=r#{mode}" 1507 assert_equal '3', f.getc, "mode=r#{mode}" 1508 assert_equal 4, f.pos, "mode=r#{mode}" 1509 assert_equal '4', f.getc, "mode=r#{mode}" 1510 end 1511 end 1512 } 1513 end 1514 1515 1516 def test_seek 1517 make_tempfile {|t| 1518 open(t.path) { |f| 1519 f.seek(9) 1520 assert_equal("az\n", f.read) 1521 } 1522 1523 open(t.path) { |f| 1524 f.seek(9, IO::SEEK_SET) 1525 assert_equal("az\n", f.read) 1526 } 1527 1528 open(t.path) { |f| 1529 f.seek(-4, IO::SEEK_END) 1530 assert_equal("baz\n", f.read) 1531 } 1532 1533 open(t.path) { |f| 1534 assert_equal("foo\n", f.gets) 1535 f.seek(2, IO::SEEK_CUR) 1536 assert_equal("r\nbaz\n", f.read) 1537 } 1538 } 1539 end 1540 1541 def test_sysseek 1542 make_tempfile {|t| 1543 open(t.path) do |f| 1544 f.sysseek(-4, IO::SEEK_END) 1545 assert_equal("baz\n", f.read) 1546 end 1547 1548 open(t.path) do |f| 1549 a = [f.getc, f.getc, f.getc] 1550 a.reverse_each {|c| f.ungetc c } 1551 assert_raise(IOError) { f.sysseek(1) } 1552 end 1553 } 1554 end 1555 1556 def test_syswrite 1557 make_tempfile {|t| 1558 open(t.path, "w") do |f| 1559 o = Object.new 1560 def o.to_s; "FOO\n"; end 1561 f.syswrite(o) 1562 end 1563 assert_equal("FOO\n", File.read(t.path)) 1564 } 1565 end 1566 1567 def test_sysread 1568 make_tempfile {|t| 1569 open(t.path) do |f| 1570 a = [f.getc, f.getc, f.getc] 1571 a.reverse_each {|c| f.ungetc c } 1572 assert_raise(IOError) { f.sysread(1) } 1573 end 1574 } 1575 end 1576 1577 def test_sysread_with_not_empty_buffer 1578 pipe(proc do |w| 1579 w.write "foob" 1580 w.close 1581 end, proc do |r| 1582 r.sysread( 5, s = "01234567" ) 1583 assert_equal( "foob", s ) 1584 end) 1585 end 1586 1587 def test_flag 1588 make_tempfile {|t| 1589 assert_raise(ArgumentError) do 1590 open(t.path, "z") { } 1591 end 1592 1593 assert_raise(ArgumentError) do 1594 open(t.path, "rr") { } 1595 end 1596 } 1597 end 1598 1599 def test_sysopen 1600 make_tempfile {|t| 1601 fd = IO.sysopen(t.path) 1602 assert_kind_of(Integer, fd) 1603 f = IO.for_fd(fd) 1604 assert_equal("foo\nbar\nbaz\n", f.read) 1605 f.close 1606 1607 fd = IO.sysopen(t.path, "w", 0666) 1608 assert_kind_of(Integer, fd) 1609 if defined?(Fcntl::F_GETFL) 1610 f = IO.for_fd(fd) 1611 else 1612 f = IO.for_fd(fd, 0666) 1613 end 1614 f.write("FOO\n") 1615 f.close 1616 1617 fd = IO.sysopen(t.path, "r") 1618 assert_kind_of(Integer, fd) 1619 f = IO.for_fd(fd) 1620 assert_equal("FOO\n", f.read) 1621 f.close 1622 } 1623 end 1624 1625 def try_fdopen(fd, autoclose = true, level = 50) 1626 if level > 0 1627 begin 1628 1.times {return try_fdopen(fd, autoclose, level - 1)} 1629 ensure 1630 GC.start 1631 end 1632 else 1633 WeakRef.new(IO.for_fd(fd, autoclose: autoclose)) 1634 end 1635 end 1636 1637 def test_autoclose 1638 feature2250 = '[ruby-core:26222]' 1639 pre = 'ft2250' 1640 1641 Dir.mktmpdir {|d| 1642 t = open("#{d}/#{pre}", "w") 1643 f = IO.for_fd(t.fileno) 1644 assert_equal(true, f.autoclose?) 1645 f.autoclose = false 1646 assert_equal(false, f.autoclose?) 1647 f.close 1648 assert_nothing_raised(Errno::EBADF, feature2250) {t.close} 1649 1650 t = open("#{d}/#{pre}", "w") 1651 f = IO.for_fd(t.fileno, autoclose: false) 1652 assert_equal(false, f.autoclose?) 1653 f.autoclose = true 1654 assert_equal(true, f.autoclose?) 1655 f.close 1656 assert_raise(Errno::EBADF, feature2250) {t.close} 1657 } 1658 end 1659 1660 def test_autoclose_true_closed_by_finalizer 1661 feature2250 = '[ruby-core:26222]' 1662 pre = 'ft2250' 1663 t = Tempfile.new(pre) 1664 w = try_fdopen(t.fileno) 1665 begin 1666 w.close 1667 begin 1668 t.close 1669 rescue Errno::EBADF 1670 end 1671 skip "expect IO object was GC'ed but not recycled yet" 1672 rescue WeakRef::RefError 1673 assert_raise(Errno::EBADF, feature2250) {t.close} 1674 end 1675 ensure 1676 t.unlink 1677 end 1678 1679 def test_autoclose_false_closed_by_finalizer 1680 feature2250 = '[ruby-core:26222]' 1681 pre = 'ft2250' 1682 t = Tempfile.new(pre) 1683 w = try_fdopen(t.fileno, false) 1684 begin 1685 w.close 1686 t.close 1687 skip "expect IO object was GC'ed but not recycled yet" 1688 rescue WeakRef::RefError 1689 assert_nothing_raised(Errno::EBADF, feature2250) {t.close} 1690 end 1691 ensure 1692 t.unlink 1693 end 1694 1695 def test_open_redirect 1696 o = Object.new 1697 def o.to_open; self; end 1698 assert_equal(o, open(o)) 1699 o2 = nil 1700 open(o) do |f| 1701 o2 = f 1702 end 1703 assert_equal(o, o2) 1704 end 1705 1706 def test_open_pipe 1707 open("|" + EnvUtil.rubybin, "r+") do |f| 1708 f.puts "puts 'foo'" 1709 f.close_write 1710 assert_equal("foo\n", f.read) 1711 end 1712 end 1713 1714 def test_reopen 1715 make_tempfile {|t| 1716 with_pipe do |r, w| 1717 assert_raise(SecurityError) do 1718 safe_4 { r.reopen(t.path) } 1719 end 1720 end 1721 1722 open(__FILE__) do |f| 1723 f.gets 1724 assert_nothing_raised { 1725 f.reopen(t.path) 1726 assert_equal("foo\n", f.gets) 1727 } 1728 end 1729 1730 open(__FILE__) do |f| 1731 f.gets 1732 f2 = open(t.path) 1733 begin 1734 f2.gets 1735 assert_nothing_raised { 1736 f.reopen(f2) 1737 assert_equal("bar\n", f.gets, '[ruby-core:24240]') 1738 } 1739 ensure 1740 f2.close 1741 end 1742 end 1743 1744 open(__FILE__) do |f| 1745 f2 = open(t.path) 1746 begin 1747 f.reopen(f2) 1748 assert_equal("foo\n", f.gets) 1749 assert_equal("bar\n", f.gets) 1750 f.reopen(f2) 1751 assert_equal("baz\n", f.gets, '[ruby-dev:39479]') 1752 ensure 1753 f2.close 1754 end 1755 end 1756 } 1757 end 1758 1759 def test_reopen_inherit 1760 mkcdtmpdir { 1761 system(EnvUtil.rubybin, '-e', <<"End") 1762 f = open("out", "w") 1763 STDOUT.reopen(f) 1764 STDERR.reopen(f) 1765 system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"') 1766 system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"') 1767End 1768 assert_equal("outerr", File.read("out")) 1769 } 1770 end 1771 1772 def test_reopen_mode 1773 feature7067 = '[ruby-core:47694]' 1774 make_tempfile {|t| 1775 open(__FILE__) do |f| 1776 assert_nothing_raised { 1777 f.reopen(t.path, "r") 1778 assert_equal("foo\n", f.gets) 1779 } 1780 end 1781 1782 open(__FILE__) do |f| 1783 assert_nothing_raised(feature7067) { 1784 f.reopen(t.path, File::RDONLY) 1785 assert_equal("foo\n", f.gets) 1786 } 1787 end 1788 } 1789 end 1790 1791 def test_reopen_opt 1792 feature7103 = '[ruby-core:47806]' 1793 make_tempfile {|t| 1794 open(__FILE__) do |f| 1795 assert_nothing_raised(feature7103) { 1796 f.reopen(t.path, "r", binmode: true) 1797 } 1798 assert_equal("foo\n", f.gets) 1799 end 1800 1801 open(__FILE__) do |f| 1802 assert_nothing_raised(feature7103) { 1803 f.reopen(t.path, autoclose: false) 1804 } 1805 assert_equal("foo\n", f.gets) 1806 end 1807 } 1808 end 1809 1810 def make_tempfile_for_encoding 1811 t = make_tempfile 1812 open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"} 1813 if block_given? 1814 yield t 1815 else 1816 t 1817 end 1818 ensure 1819 t.close(true) if t and block_given? 1820 end 1821 1822 def test_reopen_encoding 1823 make_tempfile_for_encoding {|t| 1824 open(__FILE__) {|f| 1825 f.reopen(t.path, "r:utf-8") 1826 s = f.gets 1827 assert_equal(Encoding::UTF_8, s.encoding) 1828 assert_equal("\u7d05\u7389bar\n", s) 1829 } 1830 1831 open(__FILE__) {|f| 1832 f.reopen(t.path, "r:UTF-8:EUC-JP") 1833 s = f.gets 1834 assert_equal(Encoding::EUC_JP, s.encoding) 1835 assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s) 1836 } 1837 } 1838 end 1839 1840 def test_reopen_opt_encoding 1841 feature7103 = '[ruby-core:47806]' 1842 make_tempfile_for_encoding {|t| 1843 open(__FILE__) {|f| 1844 assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "ASCII-8BIT")} 1845 s = f.gets 1846 assert_equal(Encoding::ASCII_8BIT, s.encoding) 1847 assert_equal("\xe7\xb4\x85\xe7\x8e\x89bar\n", s) 1848 } 1849 1850 open(__FILE__) {|f| 1851 assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "UTF-8:EUC-JP")} 1852 s = f.gets 1853 assert_equal(Encoding::EUC_JP, s.encoding) 1854 assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s) 1855 } 1856 } 1857 end 1858 1859 def test_foreach 1860 a = [] 1861 IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x } 1862 assert_equal(["foo\n", "bar\n", "baz\n"], a) 1863 1864 make_tempfile {|t| 1865 a = [] 1866 IO.foreach(t.path) {|x| a << x } 1867 assert_equal(["foo\n", "bar\n", "baz\n"], a) 1868 1869 a = [] 1870 IO.foreach(t.path, {:mode => "r" }) {|x| a << x } 1871 assert_equal(["foo\n", "bar\n", "baz\n"], a) 1872 1873 a = [] 1874 IO.foreach(t.path, {:open_args => [] }) {|x| a << x } 1875 assert_equal(["foo\n", "bar\n", "baz\n"], a) 1876 1877 a = [] 1878 IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x } 1879 assert_equal(["foo\n", "bar\n", "baz\n"], a) 1880 1881 a = [] 1882 IO.foreach(t.path, "b") {|x| a << x } 1883 assert_equal(["foo\nb", "ar\nb", "az\n"], a) 1884 1885 a = [] 1886 IO.foreach(t.path, 3) {|x| a << x } 1887 assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a) 1888 1889 a = [] 1890 IO.foreach(t.path, "b", 3) {|x| a << x } 1891 assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a) 1892 1893 bug = '[ruby-dev:31525]' 1894 assert_raise(ArgumentError, bug) {IO.foreach} 1895 1896 a = nil 1897 assert_nothing_raised(ArgumentError, bug) {a = IO.foreach(t.path).to_a} 1898 assert_equal(["foo\n", "bar\n", "baz\n"], a, bug) 1899 1900 bug6054 = '[ruby-dev:45267]' 1901 e = assert_raise(IOError, bug6054) {IO.foreach(t.path, mode:"w").next} 1902 assert_match(/not opened for reading/, e.message, bug6054) 1903 } 1904 end 1905 1906 def test_s_readlines 1907 make_tempfile {|t| 1908 assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path)) 1909 assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b")) 1910 assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2)) 1911 assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2)) 1912 } 1913 end 1914 1915 def test_printf 1916 pipe(proc do |w| 1917 printf(w, "foo %s baz\n", "bar") 1918 w.close_write 1919 end, proc do |r| 1920 assert_equal("foo bar baz\n", r.read) 1921 end) 1922 end 1923 1924 def test_print 1925 make_tempfile {|t| 1926 assert_in_out_err(["-", t.path], 1927 "print while $<.gets", 1928 %w(foo bar baz), []) 1929 } 1930 end 1931 1932 def test_print_separators 1933 $, = ':' 1934 $\ = "\n" 1935 pipe(proc do |w| 1936 w.print('a') 1937 w.print('a','b','c') 1938 w.close 1939 end, proc do |r| 1940 assert_equal("a\n", r.gets) 1941 assert_equal("a:b:c\n", r.gets) 1942 assert_nil r.gets 1943 r.close 1944 end) 1945 ensure 1946 $, = nil 1947 $\ = nil 1948 end 1949 1950 def test_putc 1951 pipe(proc do |w| 1952 w.putc "A" 1953 w.putc "BC" 1954 w.putc 68 1955 w.close_write 1956 end, proc do |r| 1957 assert_equal("ABD", r.read) 1958 end) 1959 1960 assert_in_out_err([], "putc 65", %w(A), []) 1961 end 1962 1963 def test_puts_recursive_array 1964 a = ["foo"] 1965 a << a 1966 pipe(proc do |w| 1967 w.puts a 1968 w.close 1969 end, proc do |r| 1970 assert_equal("foo\n[...]\n", r.read) 1971 end) 1972 end 1973 1974 def test_display 1975 pipe(proc do |w| 1976 "foo".display(w) 1977 w.close 1978 end, proc do |r| 1979 assert_equal("foo", r.read) 1980 end) 1981 1982 assert_in_out_err([], "'foo'.display", %w(foo), []) 1983 end 1984 1985 def test_set_stdout 1986 assert_raise(TypeError) { $> = Object.new } 1987 1988 assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo)) 1989 end 1990 1991 def test_initialize 1992 return unless defined?(Fcntl::F_GETFL) 1993 1994 make_tempfile {|t| 1995 1996 fd = IO.sysopen(t.path, "w") 1997 assert_kind_of(Integer, fd) 1998 %w[r r+ w+ a+].each do |mode| 1999 assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)} 2000 end 2001 f = IO.new(fd, "w") 2002 f.write("FOO\n") 2003 f.close 2004 2005 assert_equal("FOO\n", File.read(t.path)) 2006 } 2007 end 2008 2009 def test_reinitialize 2010 make_tempfile {|t| 2011 f = open(t.path) 2012 begin 2013 assert_raise(RuntimeError) do 2014 f.instance_eval { initialize } 2015 end 2016 ensure 2017 f.close 2018 end 2019 } 2020 end 2021 2022 def test_new_with_block 2023 assert_in_out_err([], "r, w = IO.pipe; IO.new(r) {}", [], /^.+$/) 2024 end 2025 2026 def test_readline2 2027 assert_in_out_err(["-e", <<-SRC], "foo\nbar\nbaz\n", %w(foo bar baz end), []) 2028 puts readline 2029 puts readline 2030 puts readline 2031 begin 2032 puts readline 2033 rescue EOFError 2034 puts "end" 2035 end 2036 SRC 2037 end 2038 2039 def test_readlines 2040 assert_in_out_err(["-e", "p readlines"], "foo\nbar\nbaz\n", 2041 ["[\"foo\\n\", \"bar\\n\", \"baz\\n\"]"], []) 2042 end 2043 2044 def test_s_read 2045 make_tempfile {|t| 2046 assert_equal("foo\nbar\nbaz\n", File.read(t.path)) 2047 assert_equal("foo\nba", File.read(t.path, 6)) 2048 assert_equal("bar\n", File.read(t.path, 4, 4)) 2049 } 2050 end 2051 2052 def test_uninitialized 2053 assert_raise(IOError) { IO.allocate.print "" } 2054 end 2055 2056 def test_nofollow 2057 # O_NOFOLLOW is not standard. 2058 return if /freebsd|linux/ !~ RUBY_PLATFORM 2059 return unless defined? File::NOFOLLOW 2060 mkcdtmpdir { 2061 open("file", "w") {|f| f << "content" } 2062 begin 2063 File.symlink("file", "slnk") 2064 rescue NotImplementedError 2065 return 2066 end 2067 assert_raise(Errno::EMLINK, Errno::ELOOP) { 2068 open("slnk", File::RDONLY|File::NOFOLLOW) {} 2069 } 2070 assert_raise(Errno::EMLINK, Errno::ELOOP) { 2071 File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {} 2072 } 2073 } 2074 end 2075 2076 def test_tainted 2077 make_tempfile {|t| 2078 assert(File.read(t.path, 4).tainted?, '[ruby-dev:38826]') 2079 assert(File.open(t.path) {|f| f.read(4)}.tainted?, '[ruby-dev:38826]') 2080 } 2081 end 2082 2083 def test_binmode_after_closed 2084 make_tempfile {|t| 2085 assert_raise(IOError) {t.binmode} 2086 } 2087 end 2088 2089 def test_threaded_flush 2090 bug3585 = '[ruby-core:31348]' 2091 src = %q{\ 2092 t = Thread.new { sleep 3 } 2093 Thread.new {sleep 1; t.kill; p 'hi!'} 2094 t.join 2095 }.gsub(/^\s+/, '') 2096 10.times.map do 2097 Thread.start do 2098 assert_in_out_err([], src) {|stdout, stderr| 2099 assert_no_match(/hi.*hi/, stderr.join, bug3585) 2100 } 2101 end 2102 end.each {|th| th.join} 2103 end 2104 2105 def test_flush_in_finalizer1 2106 require 'tempfile' 2107 bug3910 = '[ruby-dev:42341]' 2108 Tempfile.open("bug3910") {|t| 2109 path = t.path 2110 t.close 2111 fds = [] 2112 assert_nothing_raised(TypeError, bug3910) do 2113 500.times { 2114 f = File.open(path, "w") 2115 fds << f.fileno 2116 f.print "hoge" 2117 } 2118 end 2119 t.unlink 2120 } 2121 ensure 2122 GC.start 2123 end 2124 2125 def test_flush_in_finalizer2 2126 require 'tempfile' 2127 bug3910 = '[ruby-dev:42341]' 2128 Tempfile.open("bug3910") {|t| 2129 path = t.path 2130 t.close 2131 1.times do 2132 io = open(path,"w") 2133 io.print "hoge" 2134 end 2135 assert_nothing_raised(TypeError, bug3910) do 2136 GC.start 2137 end 2138 t.unlink 2139 } 2140 end 2141 2142 def test_readlines_limit_0 2143 bug4024 = '[ruby-dev:42538]' 2144 make_tempfile {|t| 2145 open(t.path, "r") do |io| 2146 assert_raise(ArgumentError, bug4024) do 2147 io.readlines(0) 2148 end 2149 end 2150 } 2151 end 2152 2153 def test_each_line_limit_0 2154 bug4024 = '[ruby-dev:42538]' 2155 make_tempfile {|t| 2156 open(t.path, "r") do |io| 2157 assert_raise(ArgumentError, bug4024) do 2158 io.each_line(0).next 2159 end 2160 end 2161 } 2162 end 2163 2164 def test_advise 2165 make_tempfile {|tf| 2166 assert_raise(ArgumentError, "no arguments") { tf.advise } 2167 %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv| 2168 [[0,0], [0, 20], [400, 2]].each do |offset, len| 2169 open(tf.path) do |t| 2170 assert_equal(t.advise(adv, offset, len), nil) 2171 assert_raise(ArgumentError, "superfluous arguments") do 2172 t.advise(adv, offset, len, offset) 2173 end 2174 assert_raise(TypeError, "wrong type for first argument") do 2175 t.advise(adv.to_s, offset, len) 2176 end 2177 assert_raise(TypeError, "wrong type for last argument") do 2178 t.advise(adv, offset, Array(len)) 2179 end 2180 assert_raise(RangeError, "last argument too big") do 2181 t.advise(adv, offset, 9999e99) 2182 end 2183 end 2184 assert_raise(IOError, "closed file") do 2185 make_tempfile {|tf2| 2186 tf2.advise(adv.to_sym, offset, len) 2187 } 2188 end 2189 end 2190 end 2191 } 2192 end 2193 2194 def test_invalid_advise 2195 feature4204 = '[ruby-dev:42887]' 2196 make_tempfile {|tf| 2197 %w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv| 2198 [[0,0], [0, 20], [400, 2]].each do |offset, len| 2199 open(tf.path) do |t| 2200 assert_raise(NotImplementedError, feature4204) { t.advise(adv, offset, len) } 2201 end 2202 end 2203 end 2204 } 2205 end 2206 2207 def test_fcntl_lock_linux 2208 return if /x86_64-linux/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform 2209 2210 pad=0 2211 Tempfile.open(self.class.name) do |f| 2212 r, w = IO.pipe 2213 pid = fork do 2214 r.close 2215 lock = [Fcntl::F_WRLCK, IO::SEEK_SET, pad, 12, 34, 0].pack("s!s!i!L!L!i!") 2216 f.fcntl Fcntl::F_SETLKW, lock 2217 w.syswrite "." 2218 sleep 2219 end 2220 w.close 2221 assert_equal ".", r.read(1) 2222 r.close 2223 pad = 0 2224 getlock = [Fcntl::F_WRLCK, 0, pad, 0, 0, 0].pack("s!s!i!L!L!i!") 2225 f.fcntl Fcntl::F_GETLK, getlock 2226 2227 ptype, whence, pad, start, len, lockpid = getlock.unpack("s!s!i!L!L!i!") 2228 2229 assert_equal(ptype, Fcntl::F_WRLCK) 2230 assert_equal(whence, IO::SEEK_SET) 2231 assert_equal(start, 12) 2232 assert_equal(len, 34) 2233 assert_equal(pid, lockpid) 2234 2235 Process.kill :TERM, pid 2236 Process.waitpid2(pid) 2237 f.close(true) 2238 end 2239 end 2240 2241 def test_fcntl_lock_freebsd 2242 return if /freebsd/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform 2243 2244 start = 12 2245 len = 34 2246 sysid = 0 2247 Tempfile.open(self.class.name) do |f| 2248 r, w = IO.pipe 2249 pid = fork do 2250 r.close 2251 lock = [start, len, 0, Fcntl::F_WRLCK, IO::SEEK_SET, sysid].pack("qqis!s!i!") 2252 f.fcntl Fcntl::F_SETLKW, lock 2253 w.syswrite "." 2254 sleep 2255 end 2256 w.close 2257 assert_equal ".", r.read(1) 2258 r.close 2259 2260 getlock = [0, 0, 0, Fcntl::F_WRLCK, 0, 0].pack("qqis!s!i!") 2261 f.fcntl Fcntl::F_GETLK, getlock 2262 2263 start, len, lockpid, ptype, whence, sysid = getlock.unpack("qqis!s!i!") 2264 2265 assert_equal(ptype, Fcntl::F_WRLCK) 2266 assert_equal(whence, IO::SEEK_SET) 2267 assert_equal(start, 12) 2268 assert_equal(len, 34) 2269 assert_equal(pid, lockpid) 2270 2271 Process.kill :TERM, pid 2272 Process.waitpid2(pid) 2273 end 2274 end 2275 2276 def test_fcntl_dupfd 2277 Tempfile.open(self.class.name) do |f| 2278 fd = f.fcntl(Fcntl::F_DUPFD, 63) 2279 begin 2280 assert_operator(fd, :>=, 63) 2281 ensure 2282 IO.for_fd(fd).close 2283 end 2284 f.unlink 2285 end 2286 end 2287 2288 def test_cross_thread_close_fd 2289 skip "cross thread close causes hung-up if pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 2290 with_pipe do |r,w| 2291 read_thread = Thread.new do 2292 begin 2293 r.read(1) 2294 rescue => e 2295 e 2296 end 2297 end 2298 2299 sleep(0.1) until read_thread.stop? 2300 r.close 2301 read_thread.join 2302 assert_kind_of(IOError, read_thread.value) 2303 end 2304 end 2305 2306 def test_cross_thread_close_stdio 2307 with_pipe do |r,w| 2308 pid = fork do 2309 $stdin.reopen(r) 2310 r.close 2311 read_thread = Thread.new do 2312 begin 2313 $stdin.read(1) 2314 rescue => e 2315 e 2316 end 2317 end 2318 sleep(0.1) until read_thread.stop? 2319 $stdin.close 2320 read_thread.join 2321 exit(IOError === read_thread.value) 2322 end 2323 assert Process.waitpid2(pid)[1].success? 2324 end 2325 rescue NotImplementedError 2326 end 2327 2328 def test_open_mode 2329 feature4742 = "[ruby-core:36338]" 2330 bug6055 = '[ruby-dev:45268]' 2331 2332 mkcdtmpdir do 2333 assert_not_nil(f = File.open('symbolic', 'w')) 2334 f.close 2335 assert_not_nil(f = File.open('numeric', File::WRONLY|File::TRUNC|File::CREAT)) 2336 f.close 2337 assert_not_nil(f = File.open('hash-symbolic', :mode => 'w')) 2338 f.close 2339 assert_not_nil(f = File.open('hash-numeric', :mode => File::WRONLY|File::TRUNC|File::CREAT), feature4742) 2340 f.close 2341 assert_nothing_raised(bug6055) {f = File.open('hash-symbolic', binmode: true)} 2342 f.close 2343 end 2344 end 2345 2346 def test_s_write 2347 mkcdtmpdir do 2348 path = "test_s_write" 2349 File.write(path, "foo\nbar\nbaz") 2350 assert_equal("foo\nbar\nbaz", File.read(path)) 2351 File.write(path, "FOO", 0) 2352 assert_equal("FOO\nbar\nbaz", File.read(path)) 2353 File.write(path, "BAR") 2354 assert_equal("BAR", File.read(path)) 2355 File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP") 2356 assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP")) 2357 File.delete path 2358 assert_equal(6, File.write(path, 'string', 2)) 2359 File.delete path 2360 assert_raise(Errno::EINVAL) { File.write('nonexisting','string', -2) } 2361 assert_equal(6, File.write(path, 'string')) 2362 assert_equal(3, File.write(path, 'sub', 1)) 2363 assert_equal("ssubng", File.read(path)) 2364 File.delete path 2365 assert_equal(3, File.write(path, "foo", encoding: "UTF-8")) 2366 File.delete path 2367 assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8")) 2368 assert_equal("foo", File.read(path)) 2369 assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8")) 2370 assert_equal("ffo", File.read(path)) 2371 File.delete path 2372 assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8")) 2373 assert_equal("\00f", File.read(path)) 2374 assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8")) 2375 assert_equal("ff", File.read(path)) 2376 end 2377 end 2378 2379 def test_s_binwrite 2380 mkcdtmpdir do 2381 path = "test_s_binwrite" 2382 File.binwrite(path, "foo\nbar\nbaz") 2383 assert_equal("foo\nbar\nbaz", File.read(path)) 2384 File.binwrite(path, "FOO", 0) 2385 assert_equal("FOO\nbar\nbaz", File.read(path)) 2386 File.binwrite(path, "BAR") 2387 assert_equal("BAR", File.read(path)) 2388 File.binwrite(path, "\u{3042}") 2389 assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path)) 2390 File.delete path 2391 assert_equal(6, File.binwrite(path, 'string', 2)) 2392 File.delete path 2393 assert_equal(6, File.binwrite(path, 'string')) 2394 assert_equal(3, File.binwrite(path, 'sub', 1)) 2395 assert_equal("ssubng", File.binread(path)) 2396 assert_equal(6, File.size(path)) 2397 assert_raise(Errno::EINVAL) { File.binwrite('nonexisting', 'string', -2) } 2398 assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") } 2399 end 2400 end 2401 2402 def test_race_between_read 2403 file = Tempfile.new("test") 2404 path = file.path 2405 file.close 2406 write_file = File.open(path, "wt") 2407 read_file = File.open(path, "rt") 2408 2409 threads = [] 2410 10.times do |i| 2411 threads << Thread.new {write_file.print(i)} 2412 threads << Thread.new {read_file.read} 2413 end 2414 threads.each {|t| t.join} 2415 assert(true, "[ruby-core:37197]") 2416 ensure 2417 read_file.close 2418 write_file.close 2419 file.close! 2420 end 2421 2422 def test_warn 2423 stderr = EnvUtil.verbose_warning do 2424 warn "warning" 2425 end 2426 assert_equal("warning\n", stderr) 2427 2428 stderr = EnvUtil.verbose_warning do 2429 warn 2430 end 2431 assert_equal("", stderr) 2432 2433 stderr = EnvUtil.verbose_warning do 2434 warn "[Feature #5029]", "[ruby-core:38070]" 2435 end 2436 assert_equal("[Feature #5029]\n[ruby-core:38070]\n", stderr) 2437 end 2438 2439 def test_cloexec 2440 return unless defined? Fcntl::FD_CLOEXEC 2441 open(__FILE__) {|f| 2442 assert(f.close_on_exec?) 2443 g = f.dup 2444 begin 2445 assert(g.close_on_exec?) 2446 f.reopen(g) 2447 assert(f.close_on_exec?) 2448 ensure 2449 g.close 2450 end 2451 g = IO.new(f.fcntl(Fcntl::F_DUPFD)) 2452 begin 2453 assert(g.close_on_exec?) 2454 ensure 2455 g.close 2456 end 2457 } 2458 IO.pipe {|r,w| 2459 assert(r.close_on_exec?) 2460 assert(w.close_on_exec?) 2461 } 2462 end 2463 2464 def test_ioctl_linux 2465 return if /linux/ !~ RUBY_PLATFORM 2466 # Alpha, mips, sparc and ppc have an another ioctl request number scheme. 2467 # So, hardcoded 0x80045200 may fail. 2468 return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM 2469 2470 assert_nothing_raised do 2471 File.open('/dev/urandom'){|f1| 2472 entropy_count = "" 2473 # RNDGETENTCNT(0x80045200) mean "get entropy count". 2474 f1.ioctl(0x80045200, entropy_count) 2475 } 2476 end 2477 2478 buf = '' 2479 assert_nothing_raised do 2480 fionread = 0x541B 2481 File.open(__FILE__){|f1| 2482 f1.ioctl(fionread, buf) 2483 } 2484 end 2485 assert_equal(File.size(__FILE__), buf.unpack('i!')[0]) 2486 end 2487 2488 def test_ioctl_linux2 2489 return if /linux/ !~ RUBY_PLATFORM 2490 return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM 2491 2492 return unless system('tty', '-s') # stdin is not a terminal 2493 File.open('/dev/tty') { |f| 2494 tiocgwinsz=0x5413 2495 winsize="" 2496 assert_nothing_raised { 2497 f.ioctl(tiocgwinsz, winsize) 2498 } 2499 } 2500 end 2501 2502 def test_setpos 2503 mkcdtmpdir { 2504 File.open("tmp.txt", "wb") {|f| 2505 f.puts "a" 2506 f.puts "bc" 2507 f.puts "def" 2508 } 2509 pos1 = pos2 = pos3 = nil 2510 File.open("tmp.txt", "rb") {|f| 2511 assert_equal("a\n", f.gets) 2512 pos1 = f.pos 2513 assert_equal("bc\n", f.gets) 2514 pos2 = f.pos 2515 assert_equal("def\n", f.gets) 2516 pos3 = f.pos 2517 assert_equal(nil, f.gets) 2518 } 2519 File.open("tmp.txt", "rb") {|f| 2520 f.pos = pos1 2521 assert_equal("bc\n", f.gets) 2522 assert_equal("def\n", f.gets) 2523 assert_equal(nil, f.gets) 2524 } 2525 File.open("tmp.txt", "rb") {|f| 2526 f.pos = pos2 2527 assert_equal("def\n", f.gets) 2528 assert_equal(nil, f.gets) 2529 } 2530 File.open("tmp.txt", "rb") {|f| 2531 f.pos = pos3 2532 assert_equal(nil, f.gets) 2533 } 2534 File.open("tmp.txt", "rb") {|f| 2535 f.pos = File.size("tmp.txt") 2536 s = "not empty string " 2537 assert_equal("", f.read(0,s)) 2538 } 2539 } 2540 end 2541 2542 def test_std_fileno 2543 assert_equal(0, STDIN.fileno) 2544 assert_equal(1, STDOUT.fileno) 2545 assert_equal(2, STDERR.fileno) 2546 assert_equal(0, $stdin.fileno) 2547 assert_equal(1, $stdout.fileno) 2548 assert_equal(2, $stderr.fileno) 2549 end 2550 2551 def test_sysread_locktmp 2552 bug6099 = '[ruby-dev:45297]' 2553 buf = " " * 100 2554 data = "a" * 100 2555 with_pipe do |r,w| 2556 th = Thread.new {r.sysread(100, buf)} 2557 Thread.pass until th.stop? 2558 buf.replace("") 2559 assert_empty(buf, bug6099) 2560 w.write(data) 2561 Thread.pass while th.alive? 2562 th.join 2563 end 2564 assert_equal(data, buf, bug6099) 2565 end 2566 2567 def test_readpartial_locktmp 2568 skip "nonblocking mode is not supported for pipe on this platform" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM 2569 bug6099 = '[ruby-dev:45297]' 2570 buf = " " * 100 2571 data = "a" * 100 2572 with_pipe do |r,w| 2573 r.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) 2574 th = Thread.new {r.readpartial(100, buf)} 2575 Thread.pass until th.stop? 2576 buf.replace("") 2577 assert_empty(buf, bug6099) 2578 w.write(data) 2579 Thread.pass while th.alive? 2580 th.join 2581 end 2582 assert_equal(data, buf, bug6099) 2583 rescue RuntimeError # can't modify string; temporarily locked 2584 end 2585 2586 def test_advise_pipe 2587 # we don't know if other platforms have a real posix_fadvise() 2588 return if /linux/ !~ RUBY_PLATFORM 2589 with_pipe do |r,w| 2590 # Linux 2.6.15 and earlier returned EINVAL instead of ESPIPE 2591 assert_raise(Errno::ESPIPE, Errno::EINVAL) { r.advise(:willneed) } 2592 assert_raise(Errno::ESPIPE, Errno::EINVAL) { w.advise(:willneed) } 2593 end 2594 end 2595 2596 def assert_buffer_not_raise_shared_string_error 2597 bug6764 = '[ruby-core:46586]' 2598 size = 28 2599 data = [*"a".."z", *"A".."Z"].shuffle.join("") 2600 t = Tempfile.new("test_io") 2601 t.write(data) 2602 t.close 2603 w = Tempfile.new("test_io") 2604 assert_nothing_raised(RuntimeError, bug6764) do 2605 File.open(t.path, "r") do |r| 2606 buf = '' 2607 while yield(r, size, buf) 2608 w << buf 2609 end 2610 end 2611 end 2612 w.close 2613 assert_equal(data, w.open.read, bug6764) 2614 ensure 2615 t.close! 2616 w.close! 2617 end 2618 2619 def test_read_buffer_not_raise_shared_string_error 2620 assert_buffer_not_raise_shared_string_error do |r, size, buf| 2621 r.read(size, buf) 2622 end 2623 end 2624 2625 def test_sysread_buffer_not_raise_shared_string_error 2626 assert_buffer_not_raise_shared_string_error do |r, size, buf| 2627 begin 2628 r.sysread(size, buf) 2629 rescue EOFError 2630 nil 2631 end 2632 end 2633 end 2634 2635 def test_readpartial_buffer_not_raise_shared_string_error 2636 assert_buffer_not_raise_shared_string_error do |r, size, buf| 2637 begin 2638 r.readpartial(size, buf) 2639 rescue EOFError 2640 nil 2641 end 2642 end 2643 end 2644 2645 def test_puts_recursive_ary 2646 bug5986 = '[ruby-core:42444]' 2647 c = Class.new { 2648 def to_ary 2649 [self] 2650 end 2651 } 2652 s = StringIO.new 2653 s.puts(c.new) 2654 assert_equal("[...]\n", s.string, bug5986) 2655 end 2656 2657 def test_io_select_with_many_files 2658 bug8080 = '[ruby-core:53349]' 2659 2660 assert_normal_exit %q{ 2661 require "tempfile" 2662 2663 # try to raise RLIM_NOFILE to >FD_SETSIZE 2664 # Unfortunately, ruby export FD_SETSIZE. then we assume it's 1024. 2665 fd_setsize = 1024 2666 2667 begin 2668 Process.setrlimit(Process::RLIMIT_NOFILE, fd_setsize+10) 2669 rescue =>e 2670 # Process::RLIMIT_NOFILE couldn't be raised. skip the test 2671 exit 0 2672 end 2673 2674 tempfiles = [] 2675 (0..fd_setsize+1).map {|i| 2676 tempfiles << Tempfile.open("test_io_select_with_many_files") 2677 } 2678 2679 IO.select(tempfiles) 2680 }, bug8080 2681 end 2682 2683 def test_read_32bit_boundary 2684 bug8431 = '[ruby-core:55098] [Bug #8431]' 2685 make_tempfile {|t| 2686 assert_separately(["-", bug8431, t.path], <<-"end;") 2687 msg = ARGV.shift 2688 f = open(ARGV[0], "rb") 2689 f.seek(0xffff_ffff) 2690 assert_nil(f.read(1), msg) 2691 end; 2692 } 2693 end if /mswin|mingw/ =~ RUBY_PLATFORM 2694 2695 def test_write_32bit_boundary 2696 bug8431 = '[ruby-core:55098] [Bug #8431]' 2697 make_tempfile {|t| 2698 def t.close(unlink_now = false) 2699 # TODO: Tempfile should deal with this delay on Windows? 2700 # NOTE: re-opening with O_TEMPORARY does not work. 2701 path = self.path 2702 ret = super 2703 if unlink_now 2704 begin 2705 File.unlink(path) 2706 rescue Errno::ENOENT 2707 rescue Errno::EACCES 2708 sleep(2) 2709 retry 2710 end 2711 end 2712 ret 2713 end 2714 2715 begin 2716 assert_separately(["-", bug8431, t.path], <<-"end;", timeout: 30) 2717 msg = ARGV.shift 2718 f = open(ARGV[0], "wb") 2719 f.seek(0xffff_ffff) 2720 begin 2721 # this will consume very long time or fail by ENOSPC on a 2722 # filesystem which sparse file is not supported 2723 f.write('1') 2724 pos = f.tell 2725 rescue Errno::ENOSPC 2726 skip "non-sparse file system" 2727 rescue SystemCallError 2728 else 2729 assert_equal(0x1_0000_0000, pos, msg) 2730 end 2731 end; 2732 rescue Timeout::Error 2733 skip "Timeout because of slow file writing" 2734 end 2735 } 2736 end if /mswin|mingw/ =~ RUBY_PLATFORM 2737 2738 def test_read_unlocktmp_ensure 2739 bug8669 = '[ruby-core:56121] [Bug #8669]' 2740 2741 str = "" 2742 r, = IO.pipe 2743 t = Thread.new { r.read(nil, str) } 2744 sleep 0.1 until t.stop? 2745 t.raise 2746 sleep 0.1 while t.alive? 2747 assert_nothing_raised(RuntimeError, bug8669) { str.clear } 2748 ensure 2749 t.kill 2750 end 2751 2752 def test_readpartial_unlocktmp_ensure 2753 bug8669 = '[ruby-core:56121] [Bug #8669]' 2754 2755 str = "" 2756 r, = IO.pipe 2757 t = Thread.new { r.readpartial(4096, str) } 2758 sleep 0.1 until t.stop? 2759 t.raise 2760 sleep 0.1 while t.alive? 2761 assert_nothing_raised(RuntimeError, bug8669) { str.clear } 2762 ensure 2763 t.kill 2764 end 2765 2766 def test_sysread_unlocktmp_ensure 2767 bug8669 = '[ruby-core:56121] [Bug #8669]' 2768 2769 str = "" 2770 r, = IO.pipe 2771 t = Thread.new { r.sysread(4096, str) } 2772 sleep 0.1 until t.stop? 2773 t.raise 2774 sleep 0.1 while t.alive? 2775 assert_nothing_raised(RuntimeError, bug8669) { str.clear } 2776 ensure 2777 t.kill 2778 end 2779end 2780