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