PageRenderTime 70ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/test/ruby/test_io.rb

http://github.com/ruby/ruby
Ruby | 3974 lines | 3592 code | 354 blank | 28 comment | 113 complexity | 3bfa2bc38b8f6ed057baf875d82c1953 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # coding: US-ASCII
  2. # frozen_string_literal: false
  3. require 'test/unit'
  4. require 'tmpdir'
  5. require "fcntl"
  6. require 'io/nonblock'
  7. require 'pathname'
  8. require 'socket'
  9. require 'stringio'
  10. require 'timeout'
  11. require 'tempfile'
  12. require 'weakref'
  13. class TestIO < Test::Unit::TestCase
  14. module Feature
  15. def have_close_on_exec?
  16. $stdin.close_on_exec?
  17. true
  18. rescue NotImplementedError
  19. false
  20. end
  21. def have_nonblock?
  22. IO.method_defined?("nonblock=")
  23. end
  24. end
  25. include Feature
  26. extend Feature
  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
  49. r&.close
  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. def with_pipe
  56. r, w = IO.pipe
  57. begin
  58. yield r, w
  59. ensure
  60. r.close
  61. w.close
  62. end
  63. end
  64. def with_read_pipe(content)
  65. pipe(proc do |w|
  66. w << content
  67. w.close
  68. end, proc do |r|
  69. yield r
  70. end)
  71. end
  72. def mkcdtmpdir
  73. Dir.mktmpdir {|d|
  74. Dir.chdir(d) {
  75. yield
  76. }
  77. }
  78. end
  79. def trapping_usr2
  80. @usr2_rcvd = 0
  81. r, w = IO.pipe
  82. trap(:USR2) do
  83. w.write([@usr2_rcvd += 1].pack('L'))
  84. end
  85. yield r
  86. ensure
  87. trap(:USR2, "DEFAULT")
  88. w&.close
  89. r&.close
  90. end
  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. def test_binmode_pipe
  107. EnvUtil.with_default_internal(Encoding::UTF_8) do
  108. EnvUtil.with_default_external(Encoding::UTF_8) do
  109. begin
  110. reader0, writer0 = IO.pipe
  111. reader0.binmode
  112. writer0.binmode
  113. reader1, writer1 = IO.pipe
  114. reader2, writer2 = IO.pipe(binmode: true)
  115. assert_predicate writer0, :binmode?
  116. assert_predicate writer2, :binmode?
  117. assert_equal writer0.binmode?, writer2.binmode?
  118. assert_equal writer0.external_encoding, writer2.external_encoding
  119. assert_equal writer0.internal_encoding, writer2.internal_encoding
  120. assert_predicate reader0, :binmode?
  121. assert_predicate reader2, :binmode?
  122. assert_equal reader0.binmode?, reader2.binmode?
  123. assert_equal reader0.external_encoding, reader2.external_encoding
  124. assert_equal reader0.internal_encoding, reader2.internal_encoding
  125. reader3, writer3 = IO.pipe("UTF-8:UTF-8", binmode: true)
  126. assert_predicate writer3, :binmode?
  127. assert_equal writer1.external_encoding, writer3.external_encoding
  128. assert_equal writer1.internal_encoding, writer3.internal_encoding
  129. assert_predicate reader3, :binmode?
  130. assert_equal reader1.external_encoding, reader3.external_encoding
  131. assert_equal reader1.internal_encoding, reader3.internal_encoding
  132. reader4, writer4 = IO.pipe("UTF-8:UTF-8", binmode: true)
  133. assert_predicate writer4, :binmode?
  134. assert_equal writer1.external_encoding, writer4.external_encoding
  135. assert_equal writer1.internal_encoding, writer4.internal_encoding
  136. assert_predicate reader4, :binmode?
  137. assert_equal reader1.external_encoding, reader4.external_encoding
  138. assert_equal reader1.internal_encoding, reader4.internal_encoding
  139. reader5, writer5 = IO.pipe("UTF-8", "UTF-8", binmode: true)
  140. assert_predicate writer5, :binmode?
  141. assert_equal writer1.external_encoding, writer5.external_encoding
  142. assert_equal writer1.internal_encoding, writer5.internal_encoding
  143. assert_predicate reader5, :binmode?
  144. assert_equal reader1.external_encoding, reader5.external_encoding
  145. assert_equal reader1.internal_encoding, reader5.internal_encoding
  146. ensure
  147. [
  148. reader0, writer0,
  149. reader1, writer1,
  150. reader2, writer2,
  151. reader3, writer3,
  152. reader4, writer4,
  153. reader5, writer5,
  154. ].compact.map(&:close)
  155. end
  156. end
  157. end
  158. end
  159. def test_pipe_block
  160. x = nil
  161. ret = IO.pipe {|r, w|
  162. x = [r,w]
  163. assert_instance_of(IO, r)
  164. assert_instance_of(IO, w)
  165. [
  166. Thread.start do
  167. w.print "abc"
  168. w.close
  169. end,
  170. Thread.start do
  171. assert_equal("abc", r.read)
  172. end
  173. ].each{|thr| thr.join}
  174. assert_not_predicate(r, :closed?)
  175. assert_predicate(w, :closed?)
  176. :foooo
  177. }
  178. assert_equal(:foooo, ret)
  179. assert_predicate(x[0], :closed?)
  180. assert_predicate(x[1], :closed?)
  181. end
  182. def test_pipe_block_close
  183. 4.times {|i|
  184. x = nil
  185. IO.pipe {|r, w|
  186. x = [r,w]
  187. r.close if (i&1) == 0
  188. w.close if (i&2) == 0
  189. }
  190. assert_predicate(x[0], :closed?)
  191. assert_predicate(x[1], :closed?)
  192. }
  193. end
  194. def test_gets_rs
  195. rs = ":"
  196. pipe(proc do |w|
  197. w.print "aaa:bbb"
  198. w.close
  199. end, proc do |r|
  200. assert_equal "aaa:", r.gets(rs)
  201. assert_equal "bbb", r.gets(rs)
  202. assert_nil r.gets(rs)
  203. r.close
  204. end)
  205. end
  206. def test_gets_default_rs
  207. pipe(proc do |w|
  208. w.print "aaa\nbbb\n"
  209. w.close
  210. end, proc do |r|
  211. assert_equal "aaa\n", r.gets
  212. assert_equal "bbb\n", r.gets
  213. assert_nil r.gets
  214. r.close
  215. end)
  216. end
  217. def test_gets_rs_nil
  218. pipe(proc do |w|
  219. w.print "a\n\nb\n\n"
  220. w.close
  221. end, proc do |r|
  222. assert_equal "a\n\nb\n\n", r.gets(nil)
  223. assert_nil r.gets("")
  224. r.close
  225. end)
  226. end
  227. def test_gets_rs_377
  228. pipe(proc do |w|
  229. w.print "\377xyz"
  230. w.close
  231. end, proc do |r|
  232. r.binmode
  233. assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]")
  234. r.close
  235. end)
  236. end
  237. def test_gets_paragraph
  238. pipe(proc do |w|
  239. w.print "a\n\nb\n\n"
  240. w.close
  241. end, proc do |r|
  242. assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]"
  243. assert_equal "b\n\n", r.gets("")
  244. assert_nil r.gets("")
  245. r.close
  246. end)
  247. end
  248. def test_gets_chomp_rs
  249. rs = ":"
  250. pipe(proc do |w|
  251. w.print "aaa:bbb"
  252. w.close
  253. end, proc do |r|
  254. assert_equal "aaa", r.gets(rs, chomp: true)
  255. assert_equal "bbb", r.gets(rs, chomp: true)
  256. assert_nil r.gets(rs, chomp: true)
  257. r.close
  258. end)
  259. end
  260. def test_gets_chomp_default_rs
  261. pipe(proc do |w|
  262. w.print "aaa\r\nbbb\nccc"
  263. w.close
  264. end, proc do |r|
  265. assert_equal "aaa", r.gets(chomp: true)
  266. assert_equal "bbb", r.gets(chomp: true)
  267. assert_equal "ccc", r.gets(chomp: true)
  268. assert_nil r.gets
  269. r.close
  270. end)
  271. (0..3).each do |i|
  272. pipe(proc do |w|
  273. w.write("a" * ((4096 << i) - 4), "\r\n" "a\r\n")
  274. w.close
  275. end,
  276. proc do |r|
  277. r.gets
  278. assert_equal "a", r.gets(chomp: true)
  279. assert_nil r.gets
  280. r.close
  281. end)
  282. end
  283. end
  284. def test_gets_chomp_rs_nil
  285. pipe(proc do |w|
  286. w.print "a\n\nb\n\n"
  287. w.close
  288. end, proc do |r|
  289. assert_equal "a\n\nb\n", r.gets(nil, chomp: true)
  290. assert_nil r.gets("")
  291. r.close
  292. end)
  293. end
  294. def test_gets_chomp_paragraph
  295. pipe(proc do |w|
  296. w.print "a\n\nb\n\n"
  297. w.close
  298. end, proc do |r|
  299. assert_equal "a", r.gets("", chomp: true)
  300. assert_equal "b", r.gets("", chomp: true)
  301. assert_nil r.gets("", chomp: true)
  302. r.close
  303. end)
  304. end
  305. def test_gets_limit_extra_arg
  306. pipe(proc do |w|
  307. w << "0123456789\n0123456789"
  308. w.close
  309. end, proc do |r|
  310. assert_equal("0123456789\n0", r.gets(nil, 12))
  311. assert_raise(TypeError) { r.gets(3,nil) }
  312. end)
  313. end
  314. # This test cause SEGV.
  315. def test_ungetc
  316. pipe(proc do |w|
  317. w.close
  318. end, proc do |r|
  319. s = "a" * 1000
  320. assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } }
  321. end)
  322. end
  323. def test_ungetbyte
  324. make_tempfile {|t|
  325. t.open
  326. t.binmode
  327. t.ungetbyte(0x41)
  328. assert_equal(-1, t.pos)
  329. assert_equal(0x41, t.getbyte)
  330. t.rewind
  331. assert_equal(0, t.pos)
  332. t.ungetbyte("qux")
  333. assert_equal(-3, t.pos)
  334. assert_equal("quxfoo\n", t.gets)
  335. assert_equal(4, t.pos)
  336. t.set_encoding("utf-8")
  337. t.ungetbyte(0x89)
  338. t.ungetbyte(0x8e)
  339. t.ungetbyte("\xe7")
  340. t.ungetbyte("\xe7\xb4\x85")
  341. assert_equal(-2, t.pos)
  342. assert_equal("\u7d05\u7389bar\n", t.gets)
  343. }
  344. end
  345. def test_each_byte
  346. pipe(proc do |w|
  347. w << "abc def"
  348. w.close
  349. end, proc do |r|
  350. r.each_byte {|byte| break if byte == 32 }
  351. assert_equal("def", r.read, "[ruby-dev:31659]")
  352. end)
  353. end
  354. def test_each_byte_with_seek
  355. make_tempfile {|t|
  356. bug5119 = '[ruby-core:38609]'
  357. i = 0
  358. open(t.path) do |f|
  359. f.each_byte {i = f.pos}
  360. end
  361. assert_equal(12, i, bug5119)
  362. }
  363. end
  364. def test_each_codepoint
  365. make_tempfile {|t|
  366. bug2959 = '[ruby-core:28650]'
  367. a = ""
  368. File.open(t, 'rt') {|f|
  369. f.each_codepoint {|c| a << c}
  370. }
  371. assert_equal("foo\nbar\nbaz\n", a, bug2959)
  372. }
  373. end
  374. def test_codepoints
  375. make_tempfile {|t|
  376. bug2959 = '[ruby-core:28650]'
  377. a = ""
  378. File.open(t, 'rt') {|f|
  379. assert_warn(/deprecated/) {
  380. f.codepoints {|c| a << c}
  381. }
  382. }
  383. assert_equal("foo\nbar\nbaz\n", a, bug2959)
  384. }
  385. end
  386. def test_rubydev33072
  387. t = make_tempfile
  388. path = t.path
  389. t.close!
  390. assert_raise(Errno::ENOENT, "[ruby-dev:33072]") do
  391. File.read(path, nil, nil, **{})
  392. end
  393. end
  394. def with_srccontent(content = "baz")
  395. src = "src"
  396. mkcdtmpdir {
  397. File.open(src, "w") {|f| f << content }
  398. yield src, content
  399. }
  400. end
  401. def test_copy_stream_small
  402. with_srccontent("foobar") {|src, content|
  403. ret = IO.copy_stream(src, "dst")
  404. assert_equal(content.bytesize, ret)
  405. assert_equal(content, File.read("dst"))
  406. }
  407. end
  408. def test_copy_stream_append
  409. with_srccontent("foobar") {|src, content|
  410. File.open('dst', 'ab') do |dst|
  411. ret = IO.copy_stream(src, dst)
  412. assert_equal(content.bytesize, ret)
  413. assert_equal(content, File.read("dst"))
  414. end
  415. }
  416. end
  417. def test_copy_stream_smaller
  418. with_srccontent {|src, content|
  419. # overwrite by smaller file.
  420. dst = "dst"
  421. File.open(dst, "w") {|f| f << "foobar"}
  422. ret = IO.copy_stream(src, dst)
  423. assert_equal(content.bytesize, ret)
  424. assert_equal(content, File.read(dst))
  425. ret = IO.copy_stream(src, dst, 2)
  426. assert_equal(2, ret)
  427. assert_equal(content[0,2], File.read(dst))
  428. ret = IO.copy_stream(src, dst, 0)
  429. assert_equal(0, ret)
  430. assert_equal("", File.read(dst))
  431. ret = IO.copy_stream(src, dst, nil, 1)
  432. assert_equal(content.bytesize-1, ret)
  433. assert_equal(content[1..-1], File.read(dst))
  434. }
  435. end
  436. def test_copy_stream_noent
  437. with_srccontent {|src, content|
  438. assert_raise(Errno::ENOENT) {
  439. IO.copy_stream("nodir/foo", "dst")
  440. }
  441. assert_raise(Errno::ENOENT) {
  442. IO.copy_stream(src, "nodir/bar")
  443. }
  444. }
  445. end
  446. def test_copy_stream_pipe
  447. with_srccontent {|src, content|
  448. pipe(proc do |w|
  449. ret = IO.copy_stream(src, w)
  450. assert_equal(content.bytesize, ret)
  451. w.close
  452. end, proc do |r|
  453. assert_equal(content, r.read)
  454. end)
  455. }
  456. end
  457. def test_copy_stream_write_pipe
  458. with_srccontent {|src, content|
  459. with_pipe {|r, w|
  460. w.close
  461. assert_raise(IOError) { IO.copy_stream(src, w) }
  462. }
  463. }
  464. end
  465. def with_pipecontent
  466. mkcdtmpdir {
  467. yield "abc"
  468. }
  469. end
  470. def test_copy_stream_pipe_to_file
  471. with_pipecontent {|pipe_content|
  472. dst = "dst"
  473. with_read_pipe(pipe_content) {|r|
  474. ret = IO.copy_stream(r, dst)
  475. assert_equal(pipe_content.bytesize, ret)
  476. assert_equal(pipe_content, File.read(dst))
  477. }
  478. }
  479. end
  480. def test_copy_stream_read_pipe
  481. with_pipecontent {|pipe_content|
  482. with_read_pipe(pipe_content) {|r1|
  483. assert_equal("a", r1.getc)
  484. pipe(proc do |w2|
  485. w2.sync = false
  486. w2 << "def"
  487. ret = IO.copy_stream(r1, w2)
  488. assert_equal(2, ret)
  489. w2.close
  490. end, proc do |r2|
  491. assert_equal("defbc", r2.read)
  492. end)
  493. }
  494. with_read_pipe(pipe_content) {|r1|
  495. assert_equal("a", r1.getc)
  496. pipe(proc do |w2|
  497. w2.sync = false
  498. w2 << "def"
  499. ret = IO.copy_stream(r1, w2, 1)
  500. assert_equal(1, ret)
  501. w2.close
  502. end, proc do |r2|
  503. assert_equal("defb", r2.read)
  504. end)
  505. }
  506. with_read_pipe(pipe_content) {|r1|
  507. assert_equal("a", r1.getc)
  508. pipe(proc do |w2|
  509. ret = IO.copy_stream(r1, w2)
  510. assert_equal(2, ret)
  511. w2.close
  512. end, proc do |r2|
  513. assert_equal("bc", r2.read)
  514. end)
  515. }
  516. with_read_pipe(pipe_content) {|r1|
  517. assert_equal("a", r1.getc)
  518. pipe(proc do |w2|
  519. ret = IO.copy_stream(r1, w2, 1)
  520. assert_equal(1, ret)
  521. w2.close
  522. end, proc do |r2|
  523. assert_equal("b", r2.read)
  524. end)
  525. }
  526. with_read_pipe(pipe_content) {|r1|
  527. assert_equal("a", r1.getc)
  528. pipe(proc do |w2|
  529. ret = IO.copy_stream(r1, w2, 0)
  530. assert_equal(0, ret)
  531. w2.close
  532. end, proc do |r2|
  533. assert_equal("", r2.read)
  534. end)
  535. }
  536. pipe(proc do |w1|
  537. w1 << "abc"
  538. w1 << "def"
  539. w1.close
  540. end, proc do |r1|
  541. assert_equal("a", r1.getc)
  542. pipe(proc do |w2|
  543. ret = IO.copy_stream(r1, w2)
  544. assert_equal(5, ret)
  545. w2.close
  546. end, proc do |r2|
  547. assert_equal("bcdef", r2.read)
  548. end)
  549. end)
  550. }
  551. end
  552. def test_copy_stream_file_to_pipe
  553. with_srccontent {|src, content|
  554. pipe(proc do |w|
  555. ret = IO.copy_stream(src, w, 1, 1)
  556. assert_equal(1, ret)
  557. w.close
  558. end, proc do |r|
  559. assert_equal(content[1,1], r.read)
  560. end)
  561. }
  562. end
  563. if have_nonblock?
  564. def test_copy_stream_no_busy_wait
  565. skip "MJIT has busy wait on GC. This sometimes fails with --jit." if RubyVM::MJIT.enabled?
  566. skip "multiple threads already active" if Thread.list.size > 1
  567. msg = 'r58534 [ruby-core:80969] [Backport #13533]'
  568. IO.pipe do |r,w|
  569. r.nonblock = true
  570. assert_cpu_usage_low(msg, stop: ->{w.close}) do
  571. IO.copy_stream(r, IO::NULL)
  572. end
  573. end
  574. end
  575. def test_copy_stream_pipe_nonblock
  576. mkcdtmpdir {
  577. with_read_pipe("abc") {|r1|
  578. assert_equal("a", r1.getc)
  579. with_pipe {|r2, w2|
  580. begin
  581. w2.nonblock = true
  582. rescue Errno::EBADF
  583. skip "nonblocking IO for pipe is not implemented"
  584. end
  585. s = w2.syswrite("a" * 100000)
  586. t = Thread.new { sleep 0.1; r2.read }
  587. ret = IO.copy_stream(r1, w2)
  588. w2.close
  589. assert_equal(2, ret)
  590. assert_equal("a" * s + "bc", t.value)
  591. }
  592. }
  593. }
  594. end
  595. end
  596. def with_bigcontent
  597. yield "abc" * 123456
  598. end
  599. def with_bigsrc
  600. mkcdtmpdir {
  601. with_bigcontent {|bigcontent|
  602. bigsrc = "bigsrc"
  603. File.open("bigsrc", "w") {|f| f << bigcontent }
  604. yield bigsrc, bigcontent
  605. }
  606. }
  607. end
  608. def test_copy_stream_bigcontent
  609. with_bigsrc {|bigsrc, bigcontent|
  610. ret = IO.copy_stream(bigsrc, "bigdst")
  611. assert_equal(bigcontent.bytesize, ret)
  612. assert_equal(bigcontent, File.read("bigdst"))
  613. }
  614. end
  615. def test_copy_stream_bigcontent_chop
  616. with_bigsrc {|bigsrc, bigcontent|
  617. ret = IO.copy_stream(bigsrc, "bigdst", nil, 100)
  618. assert_equal(bigcontent.bytesize-100, ret)
  619. assert_equal(bigcontent[100..-1], File.read("bigdst"))
  620. }
  621. end
  622. def test_copy_stream_bigcontent_mid
  623. with_bigsrc {|bigsrc, bigcontent|
  624. ret = IO.copy_stream(bigsrc, "bigdst", 30000, 100)
  625. assert_equal(30000, ret)
  626. assert_equal(bigcontent[100, 30000], File.read("bigdst"))
  627. }
  628. end
  629. def test_copy_stream_bigcontent_fpos
  630. with_bigsrc {|bigsrc, bigcontent|
  631. File.open(bigsrc) {|f|
  632. begin
  633. assert_equal(0, f.pos)
  634. ret = IO.copy_stream(f, "bigdst", nil, 10)
  635. assert_equal(bigcontent.bytesize-10, ret)
  636. assert_equal(bigcontent[10..-1], File.read("bigdst"))
  637. assert_equal(0, f.pos)
  638. ret = IO.copy_stream(f, "bigdst", 40, 30)
  639. assert_equal(40, ret)
  640. assert_equal(bigcontent[30, 40], File.read("bigdst"))
  641. assert_equal(0, f.pos)
  642. rescue NotImplementedError
  643. #skip "pread(2) is not implemented."
  644. end
  645. }
  646. }
  647. end
  648. def test_copy_stream_closed_pipe
  649. with_srccontent {|src,|
  650. with_pipe {|r, w|
  651. w.close
  652. assert_raise(IOError) { IO.copy_stream(src, w) }
  653. }
  654. }
  655. end
  656. def with_megacontent
  657. yield "abc" * 1234567
  658. end
  659. def with_megasrc
  660. mkcdtmpdir {
  661. with_megacontent {|megacontent|
  662. megasrc = "megasrc"
  663. File.open(megasrc, "w") {|f| f << megacontent }
  664. yield megasrc, megacontent
  665. }
  666. }
  667. end
  668. if have_nonblock?
  669. def test_copy_stream_megacontent_nonblock
  670. with_megacontent {|megacontent|
  671. with_pipe {|r1, w1|
  672. with_pipe {|r2, w2|
  673. begin
  674. r1.nonblock = true
  675. w2.nonblock = true
  676. rescue Errno::EBADF
  677. skip "nonblocking IO for pipe is not implemented"
  678. end
  679. t1 = Thread.new { w1 << megacontent; w1.close }
  680. t2 = Thread.new { r2.read }
  681. t3 = Thread.new {
  682. ret = IO.copy_stream(r1, w2)
  683. assert_equal(megacontent.bytesize, ret)
  684. w2.close
  685. }
  686. _, t2_value, _ = assert_join_threads([t1, t2, t3])
  687. assert_equal(megacontent, t2_value)
  688. }
  689. }
  690. }
  691. end
  692. end
  693. def test_copy_stream_megacontent_pipe_to_file
  694. with_megasrc {|megasrc, megacontent|
  695. with_pipe {|r1, w1|
  696. with_pipe {|r2, w2|
  697. t1 = Thread.new { w1 << megacontent; w1.close }
  698. t2 = Thread.new { r2.read }
  699. t3 = Thread.new {
  700. ret = IO.copy_stream(r1, w2)
  701. assert_equal(megacontent.bytesize, ret)
  702. w2.close
  703. }
  704. _, t2_value, _ = assert_join_threads([t1, t2, t3])
  705. assert_equal(megacontent, t2_value)
  706. }
  707. }
  708. }
  709. end
  710. def test_copy_stream_megacontent_file_to_pipe
  711. with_megasrc {|megasrc, megacontent|
  712. with_pipe {|r, w|
  713. t1 = Thread.new { r.read }
  714. t2 = Thread.new {
  715. ret = IO.copy_stream(megasrc, w)
  716. assert_equal(megacontent.bytesize, ret)
  717. w.close
  718. }
  719. t1_value, _ = assert_join_threads([t1, t2])
  720. assert_equal(megacontent, t1_value)
  721. }
  722. }
  723. end
  724. def test_copy_stream_rbuf
  725. mkcdtmpdir {
  726. begin
  727. pipe(proc do |w|
  728. File.open("foo", "w") {|f| f << "abcd" }
  729. File.open("foo") {|f|
  730. f.read(1)
  731. assert_equal(3, IO.copy_stream(f, w, 10, 1))
  732. }
  733. w.close
  734. end, proc do |r|
  735. assert_equal("bcd", r.read)
  736. end)
  737. rescue NotImplementedError
  738. skip "pread(2) is not implemtented."
  739. end
  740. }
  741. end
  742. def with_socketpair
  743. s1, s2 = UNIXSocket.pair
  744. begin
  745. yield s1, s2
  746. ensure
  747. s1.close unless s1.closed?
  748. s2.close unless s2.closed?
  749. end
  750. end
  751. def test_copy_stream_socket1
  752. with_srccontent("foobar") {|src, content|
  753. with_socketpair {|s1, s2|
  754. ret = IO.copy_stream(src, s1)
  755. assert_equal(content.bytesize, ret)
  756. s1.close
  757. assert_equal(content, s2.read)
  758. }
  759. }
  760. end if defined? UNIXSocket
  761. def test_copy_stream_socket2
  762. with_bigsrc {|bigsrc, bigcontent|
  763. with_socketpair {|s1, s2|
  764. t1 = Thread.new { s2.read }
  765. t2 = Thread.new {
  766. ret = IO.copy_stream(bigsrc, s1)
  767. assert_equal(bigcontent.bytesize, ret)
  768. s1.close
  769. }
  770. result, _ = assert_join_threads([t1, t2])
  771. assert_equal(bigcontent, result)
  772. }
  773. }
  774. end if defined? UNIXSocket
  775. def test_copy_stream_socket3
  776. with_bigsrc {|bigsrc, bigcontent|
  777. with_socketpair {|s1, s2|
  778. t1 = Thread.new { s2.read }
  779. t2 = Thread.new {
  780. ret = IO.copy_stream(bigsrc, s1, 10000)
  781. assert_equal(10000, ret)
  782. s1.close
  783. }
  784. result, _ = assert_join_threads([t1, t2])
  785. assert_equal(bigcontent[0,10000], result)
  786. }
  787. }
  788. end if defined? UNIXSocket
  789. def test_copy_stream_socket4
  790. with_bigsrc {|bigsrc, bigcontent|
  791. File.open(bigsrc) {|f|
  792. assert_equal(0, f.pos)
  793. with_socketpair {|s1, s2|
  794. t1 = Thread.new { s2.read }
  795. t2 = Thread.new {
  796. ret = IO.copy_stream(f, s1, nil, 100)
  797. assert_equal(bigcontent.bytesize-100, ret)
  798. assert_equal(0, f.pos)
  799. s1.close
  800. }
  801. result, _ = assert_join_threads([t1, t2])
  802. assert_equal(bigcontent[100..-1], result)
  803. }
  804. }
  805. }
  806. end if defined? UNIXSocket
  807. def test_copy_stream_socket5
  808. with_bigsrc {|bigsrc, bigcontent|
  809. File.open(bigsrc) {|f|
  810. assert_equal(bigcontent[0,100], f.read(100))
  811. assert_equal(100, f.pos)
  812. with_socketpair {|s1, s2|
  813. t1 = Thread.new { s2.read }
  814. t2 = Thread.new {
  815. ret = IO.copy_stream(f, s1)
  816. assert_equal(bigcontent.bytesize-100, ret)
  817. assert_equal(bigcontent.length, f.pos)
  818. s1.close
  819. }
  820. result, _ = assert_join_threads([t1, t2])
  821. assert_equal(bigcontent[100..-1], result)
  822. }
  823. }
  824. }
  825. end if defined? UNIXSocket
  826. def test_copy_stream_socket6
  827. mkcdtmpdir {
  828. megacontent = "abc" * 1234567
  829. File.open("megasrc", "w") {|f| f << megacontent }
  830. with_socketpair {|s1, s2|
  831. begin
  832. s1.nonblock = true
  833. rescue Errno::EBADF
  834. skip "nonblocking IO for pipe is not implemented"
  835. end
  836. t1 = Thread.new { s2.read }
  837. t2 = Thread.new {
  838. ret = IO.copy_stream("megasrc", s1)
  839. assert_equal(megacontent.bytesize, ret)
  840. s1.close
  841. }
  842. result, _ = assert_join_threads([t1, t2])
  843. assert_equal(megacontent, result)
  844. }
  845. }
  846. end if defined? UNIXSocket
  847. def test_copy_stream_socket7
  848. GC.start
  849. mkcdtmpdir {
  850. megacontent = "abc" * 1234567
  851. File.open("megasrc", "w") {|f| f << megacontent }
  852. with_socketpair {|s1, s2|
  853. begin
  854. s1.nonblock = true
  855. rescue Errno::EBADF
  856. skip "nonblocking IO for pipe is not implemented"
  857. end
  858. trapping_usr2 do |rd|
  859. nr = 30
  860. begin
  861. pid = fork do
  862. s1.close
  863. IO.select([s2])
  864. Process.kill(:USR2, Process.ppid)
  865. buf = String.new(capacity: 16384)
  866. nil while s2.read(16384, buf)
  867. end
  868. s2.close
  869. nr.times do
  870. assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1)
  871. end
  872. assert_equal(1, rd.read(4).unpack1('L'))
  873. ensure
  874. s1.close
  875. _, status = Process.waitpid2(pid) if pid
  876. end
  877. assert_predicate(status, :success?)
  878. end
  879. }
  880. }
  881. end if defined? UNIXSocket and IO.method_defined?("nonblock=")
  882. def test_copy_stream_strio
  883. src = StringIO.new("abcd")
  884. dst = StringIO.new
  885. ret = IO.copy_stream(src, dst)
  886. assert_equal(4, ret)
  887. assert_equal("abcd", dst.string)
  888. assert_equal(4, src.pos)
  889. end
  890. def test_copy_stream_strio_len
  891. src = StringIO.new("abcd")
  892. dst = StringIO.new
  893. ret = IO.copy_stream(src, dst, 3)
  894. assert_equal(3, ret)
  895. assert_equal("abc", dst.string)
  896. assert_equal(3, src.pos)
  897. end
  898. def test_copy_stream_strio_off
  899. src = StringIO.new("abcd")
  900. with_pipe {|r, w|
  901. assert_raise(ArgumentError) {
  902. IO.copy_stream(src, w, 3, 1)
  903. }
  904. }
  905. end
  906. def test_copy_stream_fname_to_strio
  907. mkcdtmpdir {
  908. File.open("foo", "w") {|f| f << "abcd" }
  909. src = "foo"
  910. dst = StringIO.new
  911. ret = IO.copy_stream(src, dst, 3)
  912. assert_equal(3, ret)
  913. assert_equal("abc", dst.string)
  914. }
  915. end
  916. def test_copy_stream_strio_to_fname
  917. mkcdtmpdir {
  918. # StringIO to filename
  919. src = StringIO.new("abcd")
  920. ret = IO.copy_stream(src, "fooo", 3)
  921. assert_equal(3, ret)
  922. assert_equal("abc", File.read("fooo"))
  923. assert_equal(3, src.pos)
  924. }
  925. end
  926. def test_copy_stream_io_to_strio
  927. mkcdtmpdir {
  928. # IO to StringIO
  929. File.open("bar", "w") {|f| f << "abcd" }
  930. File.open("bar") {|src|
  931. dst = StringIO.new
  932. ret = IO.copy_stream(src, dst, 3)
  933. assert_equal(3, ret)
  934. assert_equal("abc", dst.string)
  935. assert_equal(3, src.pos)
  936. }
  937. }
  938. end
  939. def test_copy_stream_strio_to_io
  940. mkcdtmpdir {
  941. # StringIO to IO
  942. src = StringIO.new("abcd")
  943. ret = File.open("baz", "w") {|dst|
  944. IO.copy_stream(src, dst, 3)
  945. }
  946. assert_equal(3, ret)
  947. assert_equal("abc", File.read("baz"))
  948. assert_equal(3, src.pos)
  949. }
  950. end
  951. def test_copy_stream_strio_to_tempfile
  952. bug11015 = '[ruby-core:68676] [Bug #11015]'
  953. # StringIO to Tempfile
  954. src = StringIO.new("abcd")
  955. dst = Tempfile.new("baz")
  956. ret = IO.copy_stream(src, dst)
  957. assert_equal(4, ret)
  958. pos = dst.pos
  959. dst.rewind
  960. assert_equal("abcd", dst.read)
  961. assert_equal(4, pos, bug11015)
  962. ensure
  963. dst.close!
  964. end
  965. def test_copy_stream_pathname_to_pathname
  966. bug11199 = '[ruby-dev:49008] [Bug #11199]'
  967. mkcdtmpdir {
  968. File.open("src", "w") {|f| f << "ok" }
  969. src = Pathname.new("src")
  970. dst = Pathname.new("dst")
  971. IO.copy_stream(src, dst)
  972. assert_equal("ok", IO.read("dst"), bug11199)
  973. }
  974. end
  975. def test_copy_stream_write_in_binmode
  976. bug8767 = '[ruby-core:56518] [Bug #8767]'
  977. mkcdtmpdir {
  978. EnvUtil.with_default_internal(Encoding::UTF_8) do
  979. # StringIO to object with to_path
  980. bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
  981. src = StringIO.new(bytes)
  982. dst = Object.new
  983. def dst.to_path
  984. "qux"
  985. end
  986. assert_nothing_raised(bug8767) {
  987. IO.copy_stream(src, dst)
  988. }
  989. assert_equal(bytes, File.binread("qux"), bug8767)
  990. assert_equal(4, src.pos, bug8767)
  991. end
  992. }
  993. end
  994. def test_copy_stream_read_in_binmode
  995. bug8767 = '[ruby-core:56518] [Bug #8767]'
  996. mkcdtmpdir {
  997. EnvUtil.with_default_internal(Encoding::UTF_8) do
  998. # StringIO to object with to_path
  999. bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
  1000. File.binwrite("qux", bytes)
  1001. dst = StringIO.new
  1002. src = Object.new
  1003. def src.to_path
  1004. "qux"
  1005. end
  1006. assert_nothing_raised(bug8767) {
  1007. IO.copy_stream(src, dst)
  1008. }
  1009. assert_equal(bytes, dst.string.b, bug8767)
  1010. assert_equal(4, dst.pos, bug8767)
  1011. end
  1012. }
  1013. end
  1014. class Rot13IO
  1015. def initialize(io)
  1016. @io = io
  1017. end
  1018. def readpartial(*args)
  1019. ret = @io.readpartial(*args)
  1020. ret.tr!('a-zA-Z', 'n-za-mN-ZA-M')
  1021. ret
  1022. end
  1023. def write(str)
  1024. @io.write(str.tr('a-zA-Z', 'n-za-mN-ZA-M'))
  1025. end
  1026. def to_io
  1027. @io
  1028. end
  1029. end
  1030. def test_copy_stream_io_to_rot13
  1031. mkcdtmpdir {
  1032. File.open("bar", "w") {|f| f << "vex" }
  1033. File.open("bar") {|src|
  1034. File.open("baz", "w") {|dst0|
  1035. dst = Rot13IO.new(dst0)
  1036. ret = IO.copy_stream(src, dst, 3)
  1037. assert_equal(3, ret)
  1038. }
  1039. assert_equal("irk", File.read("baz"))
  1040. }
  1041. }
  1042. end
  1043. def test_copy_stream_rot13_to_io
  1044. mkcdtmpdir {
  1045. File.open("bar", "w") {|f| f << "flap" }
  1046. File.open("bar") {|src0|
  1047. src = Rot13IO.new(src0)
  1048. File.open("baz", "w") {|dst|
  1049. ret = IO.copy_stream(src, dst, 4)
  1050. assert_equal(4, ret)
  1051. }
  1052. }
  1053. assert_equal("sync", File.read("baz"))
  1054. }
  1055. end
  1056. def test_copy_stream_rot13_to_rot13
  1057. mkcdtmpdir {
  1058. File.open("bar", "w") {|f| f << "bin" }
  1059. File.open("bar") {|src0|
  1060. src = Rot13IO.new(src0)
  1061. File.open("baz", "w") {|dst0|
  1062. dst = Rot13IO.new(dst0)
  1063. ret = IO.copy_stream(src, dst, 3)
  1064. assert_equal(3, ret)
  1065. }
  1066. }
  1067. assert_equal("bin", File.read("baz"))
  1068. }
  1069. end
  1070. def test_copy_stream_strio_flush
  1071. with_pipe {|r, w|
  1072. w.sync = false
  1073. w.write "zz"
  1074. src = StringIO.new("abcd")
  1075. IO.copy_stream(src, w)
  1076. t1 = Thread.new {
  1077. w.close
  1078. }
  1079. t2 = Thread.new { r.read }
  1080. _, result = assert_join_threads([t1, t2])
  1081. assert_equal("zzabcd", result)
  1082. }
  1083. end
  1084. def test_copy_stream_strio_rbuf
  1085. pipe(proc do |w|
  1086. w << "abcd"
  1087. w.close
  1088. end, proc do |r|
  1089. assert_equal("a", r.read(1))
  1090. sio = StringIO.new
  1091. IO.copy_stream(r, sio)
  1092. assert_equal("bcd", sio.string)
  1093. end)
  1094. end
  1095. def test_copy_stream_src_wbuf
  1096. mkcdtmpdir {
  1097. pipe(proc do |w|
  1098. File.open("foe", "w+") {|f|
  1099. f.write "abcd\n"
  1100. f.rewind
  1101. f.write "xy"
  1102. IO.copy_stream(f, w)
  1103. }
  1104. assert_equal("xycd\n", File.read("foe"))
  1105. w.close
  1106. end, proc do |r|
  1107. assert_equal("cd\n", r.read)
  1108. r.close
  1109. end)
  1110. }
  1111. end
  1112. class Bug5237
  1113. attr_reader :count
  1114. def initialize
  1115. @count = 0
  1116. end
  1117. def read(bytes, buffer)
  1118. @count += 1
  1119. buffer.replace "this is a test"
  1120. nil
  1121. end
  1122. end
  1123. def test_copy_stream_broken_src_read_eof
  1124. src = Bug5237.new
  1125. dst = StringIO.new
  1126. assert_equal 0, src.count
  1127. th = Thread.new { IO.copy_stream(src, dst) }
  1128. flunk("timeout") unless th.join(10)
  1129. assert_equal 1, src.count
  1130. end
  1131. def test_copy_stream_dst_rbuf
  1132. mkcdtmpdir {
  1133. pipe(proc do |w|
  1134. w << "xyz"
  1135. w.close
  1136. end, proc do |r|
  1137. File.open("fom", "w+b") {|f|
  1138. f.write "abcd\n"
  1139. f.rewind
  1140. assert_equal("abc", f.read(3))
  1141. f.ungetc "c"
  1142. IO.copy_stream(r, f)
  1143. }
  1144. assert_equal("abxyz", File.read("fom"))
  1145. end)
  1146. }
  1147. end
  1148. def test_copy_stream_to_duplex_io
  1149. result = IO.pipe {|a,w|
  1150. th = Thread.start {w.puts "yes"; w.close}
  1151. IO.popen([EnvUtil.rubybin, '-pe$_="#$.:#$_"'], "r+") {|b|
  1152. IO.copy_stream(a, b)
  1153. b.close_write
  1154. assert_join_threads([th])
  1155. b.read
  1156. }
  1157. }
  1158. assert_equal("1:yes\n", result)
  1159. end
  1160. def ruby(*args)
  1161. args = ['-e', '$>.write($<.read)'] if args.empty?
  1162. ruby = EnvUtil.rubybin
  1163. opts = {}
  1164. if defined?(Process::RLIMIT_NPROC)
  1165. lim = Process.getrlimit(Process::RLIMIT_NPROC)[1]
  1166. opts[:rlimit_nproc] = [lim, 2048].min
  1167. end
  1168. f = IO.popen([ruby] + args, 'r+', opts)
  1169. pid = f.pid
  1170. yield(f)
  1171. ensure
  1172. f.close unless !f || f.closed?
  1173. begin
  1174. Process.wait(pid)
  1175. rescue Errno::ECHILD, Errno::ESRCH
  1176. end
  1177. end
  1178. def test_try_convert
  1179. assert_equal(STDOUT, IO.try_convert(STDOUT))
  1180. assert_equal(nil, IO.try_convert("STDOUT"))
  1181. end
  1182. def test_ungetc2
  1183. f = false
  1184. pipe(proc do |w|
  1185. Thread.pass until f
  1186. w.write("1" * 10000)
  1187. w.close
  1188. end, proc do |r|
  1189. r.ungetc("0" * 10000)
  1190. f = true
  1191. assert_equal("0" * 10000 + "1" * 10000, r.read)
  1192. end)
  1193. end
  1194. def test_write_with_multiple_arguments
  1195. pipe(proc do |w|
  1196. w.write("foo", "bar")
  1197. w.close
  1198. end, proc do |r|
  1199. assert_equal("foobar", r.read)
  1200. end)
  1201. end
  1202. def test_write_with_multiple_arguments_and_buffer
  1203. mkcdtmpdir do
  1204. line = "x"*9+"\n"
  1205. file = "test.out"
  1206. open(file, "wb") do |w|
  1207. w.write(line)
  1208. assert_equal(11, w.write(line, "\n"))
  1209. end
  1210. open(file, "rb") do |r|
  1211. assert_equal([line, line, "\n"], r.readlines)
  1212. end
  1213. line = "x"*99+"\n"
  1214. open(file, "wb") do |w|
  1215. w.write(line*81) # 8100 bytes
  1216. assert_equal(100, w.write("a"*99, "\n"))
  1217. end
  1218. open(file, "rb") do |r|
  1219. 81.times {assert_equal(line, r.gets)}
  1220. assert_equal("a"*99+"\n", r.gets)
  1221. end
  1222. end
  1223. end
  1224. def test_write_with_many_arguments
  1225. [1023, 1024].each do |n|
  1226. pipe(proc do |w|
  1227. w.write(*(["a"] * n))
  1228. w.close
  1229. end, proc do |r|
  1230. assert_equal("a" * n, r.read)
  1231. end)
  1232. end
  1233. end
  1234. def test_write_with_multiple_nonstring_arguments
  1235. assert_in_out_err([], "STDOUT.write(:foo, :bar)", ["foobar"])
  1236. end
  1237. def test_write_buffered_with_multiple_arguments
  1238. out, err, (_, status) = EnvUtil.invoke_ruby(["-e", "sleep 0.1;puts 'foo'"], "", true, true) do |_, o, e, i|
  1239. [o.read, e.read, Process.waitpid2(i)]
  1240. end
  1241. assert_predicate(status, :success?)
  1242. assert_equal("foo\n", out)
  1243. assert_empty(err)
  1244. end
  1245. def test_write_no_args
  1246. IO.pipe do |r, w|
  1247. assert_equal 0, w.write, '[ruby-core:86285] [Bug #14338]'
  1248. assert_equal :wait_readable, r.read_nonblock(1, exception: false)
  1249. end
  1250. end
  1251. def test_write_non_writable
  1252. with_pipe do |r, w|
  1253. assert_raise(IOError) do
  1254. r.write "foobarbaz"
  1255. end
  1256. end
  1257. end
  1258. def test_dup
  1259. ruby do |f|
  1260. begin
  1261. f2 = f.dup
  1262. f.puts "foo"
  1263. f2.puts "bar"
  1264. f.close_write
  1265. f2.close_write
  1266. assert_equal("foo\nbar\n", f.read)
  1267. assert_equal("", f2.read)
  1268. ensure
  1269. f2.close
  1270. end
  1271. end
  1272. end
  1273. def test_dup_many
  1274. opts = {}
  1275. opts[:rlimit_nofile] = 1024 if defined?(Process::RLIMIT_NOFILE)
  1276. assert_separately([], <<-'End', **opts)
  1277. a = []
  1278. assert_raise(Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM) do
  1279. loop {a << IO.pipe}
  1280. end
  1281. assert_raise(Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM) do
  1282. loop {a << [a[-1][0].dup, a[-1][1].dup]}
  1283. end
  1284. End
  1285. end
  1286. def test_inspect
  1287. with_pipe do |r, w|
  1288. assert_match(/^#<IO:fd \d+>$/, r.inspect)
  1289. r.freeze
  1290. assert_match(/^#<IO:fd \d+>$/, r.inspect)
  1291. end
  1292. end
  1293. def test_readpartial
  1294. pipe(proc do |w|
  1295. w.write "foobarbaz"
  1296. w.close
  1297. end, proc do |r|
  1298. assert_raise(ArgumentError) { r.readpartial(-1) }
  1299. assert_equal("fooba", r.readpartial(5))
  1300. r.readpartial(5, s = "")
  1301. assert_equal("rbaz", s)
  1302. end)
  1303. end
  1304. def test_readpartial_lock
  1305. with_pipe do |r, w|
  1306. s = ""
  1307. t = Thread.new { r.readpartial(5, s) }
  1308. Thread.pass until t.stop?
  1309. assert_raise(RuntimeError) { s.clear }
  1310. w.write "foobarbaz"
  1311. w.close
  1312. assert_equal("fooba", t.value)
  1313. end
  1314. end
  1315. def test_readpartial_pos
  1316. mkcdtmpdir {
  1317. open("foo", "w") {|f| f << "abc" }
  1318. open("foo") {|f|
  1319. f.seek(0)
  1320. assert_equal("ab", f.readpartial(2))
  1321. assert_equal(2, f.pos)
  1322. }
  1323. }
  1324. end
  1325. def test_readpartial_with_not_empty_buffer
  1326. pipe(proc do |w|
  1327. w.write "foob"
  1328. w.close
  1329. end, proc do |r|
  1330. r.readpartial(5, s = "01234567")
  1331. assert_equal("foob", s)
  1332. end)
  1333. end
  1334. def test_readpartial_buffer_error
  1335. with_pipe do |r, w|
  1336. s = ""
  1337. t = Thread.new { r.readpartial(5, s) }
  1338. Thread.pass until t.stop?
  1339. t.kill
  1340. t.value
  1341. assert_equal("", s)
  1342. end
  1343. end if /cygwin/ !~ RUBY_PLATFORM
  1344. def test_read
  1345. pipe(proc do |w|
  1346. w.write "foobarbaz"
  1347. w.close
  1348. end, proc do |r|
  1349. assert_raise(ArgumentError) { r.read(-1) }
  1350. assert_equal("fooba", r.read(5))
  1351. r.read(nil, s = "")
  1352. assert_equal("rbaz", s)
  1353. end)
  1354. end
  1355. def test_read_lock
  1356. with_pipe do |r, w|
  1357. s = ""
  1358. t = Thread.new { r.read(5, s) }
  1359. Thread.pass until t.stop?
  1360. assert_raise(RuntimeError) { s.clear }
  1361. w.write "foobarbaz"
  1362. w.close
  1363. assert_equal("fooba", t.value)
  1364. end
  1365. end
  1366. def test_read_with_not_empty_buffer
  1367. pipe(proc do |w|
  1368. w.write "foob"
  1369. w.close
  1370. end, proc do |r|
  1371. r.read(nil, s = "01234567")
  1372. assert_equal("foob", s)
  1373. end)
  1374. end
  1375. def test_read_buffer_error
  1376. with_pipe do |r, w|
  1377. s = ""
  1378. t = Thread.new { r.read(5, s) }
  1379. Thread.pass until t.stop?
  1380. t.kill
  1381. t.value
  1382. assert_equal("", s)
  1383. end
  1384. with_pipe do |r, w|
  1385. s = "xxx"
  1386. t = Thread.new {r.read(2, s)}
  1387. Thread.pass until t.stop?
  1388. t.kill
  1389. t.value
  1390. assert_equal("xxx", s)
  1391. end
  1392. end if /cygwin/ !~ RUBY_PLATFORM
  1393. def test_write_nonblock
  1394. pipe(proc do |w|
  1395. w.write_nonblock(1)
  1396. w.close
  1397. end, proc do |r|
  1398. assert_equal("1", r.read)
  1399. end)
  1400. end
  1401. def test_read_nonblock_with_not_empty_buffer
  1402. with_pipe {|r, w|
  1403. w.write "foob"
  1404. w.close
  1405. r.read_nonblock(5, s = "01234567")
  1406. assert_equal("foob", s)
  1407. }
  1408. end
  1409. def test_write_nonblock_simple_no_exceptions
  1410. pipe(proc do |w|
  1411. w.write_nonblock('1', exception: false)
  1412. w.close
  1413. end, proc do |r|
  1414. assert_equal("1", r.read)
  1415. end)
  1416. end
  1417. def test_read_nonblock_error
  1418. with_pipe {|r, w|
  1419. begin
  1420. r.read_nonblock 4096
  1421. rescue Errno::EWOULDBLOCK
  1422. assert_kind_of(IO::WaitReadable, $!)
  1423. end
  1424. }
  1425. with_pipe {|r, w|
  1426. begin
  1427. r.read_nonblock 4096, ""
  1428. rescue Errno::EWOULDBLOCK
  1429. assert_kind_of(IO::WaitReadable, $!)
  1430. end
  1431. }
  1432. end if have_nonblock?
  1433. def test_read_nonblock_invalid_exception
  1434. with_pipe {|r, w|
  1435. assert_raise(ArgumentError) {r.read_nonblock(4096, exception: 1)}
  1436. }
  1437. end if have_nonblock?
  1438. def test_read_nonblock_no_exceptions
  1439. skip '[ruby-core:90895] MJIT worker may leave fd open in a forked child' if RubyVM::MJIT.enabled? # TODO: consider acquiring GVL from MJIT worker.
  1440. with_pipe {|r, w|
  1441. assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
  1442. w.puts "HI!"
  1443. assert_equal "HI!\n", r.read_nonblock(4096, exception: false)
  1444. w.close
  1445. assert_equal nil, r.read_nonblock(4096, exception: false)
  1446. }
  1447. end if have_nonblock?
  1448. def test_read_nonblock_with_buffer_no_exceptions
  1449. with_pipe {|r, w|
  1450. assert_equal :wait_readable, r.read_nonblock(4096, "", exception: false)
  1451. w.puts "HI!"
  1452. buf = "buf"
  1453. value = r.read_nonblock(4096, buf, exception: false)
  1454. assert_equal value, "HI!\n"
  1455. assert_same(buf, value)
  1456. w.close
  1457. assert_equal nil, r.read_nonblock(4096, "", exception: false)
  1458. }
  1459. end if have_nonblock?
  1460. def test_write_nonblock_error
  1461. with_pipe {|r, w|
  1462. begin
  1463. loop {
  1464. w.write_nonblock "a"*100000
  1465. }
  1466. rescue Errno::EWOULDBLOCK
  1467. assert_kind_of(IO::WaitWritable, $!)
  1468. end
  1469. }
  1470. end if have_nonblock?
  1471. def test_write_nonblock_invalid_exception
  1472. with_pipe {|r, w|
  1473. assert_raise(ArgumentError) {w.write_nonblock(4096, exception: 1)}
  1474. }
  1475. end if have_nonblock?
  1476. def test_write_nonblock_no_exceptions
  1477. with_pipe {|r, w|
  1478. loop {
  1479. ret = w.write_nonblock("a"*100000, exception: false)
  1480. if ret.is_a?(Symbol)
  1481. assert_equal :wait_writable, ret
  1482. break
  1483. end
  1484. }
  1485. }
  1486. end if have_nonblock?
  1487. def test_gets
  1488. pipe(proc do |w|
  1489. w.write "foobarbaz"
  1490. w.close
  1491. end, proc do |r|
  1492. assert_equal("", r.gets(0))
  1493. assert_equal("foobarbaz", r.gets(9))
  1494. end)
  1495. end
  1496. def test_close_read
  1497. ruby do |f|
  1498. f.close_read
  1499. f.write "foobarbaz"
  1500. assert_raise(IOError) { f.read }
  1501. assert_nothing_raised(IOError) {f.close_read}
  1502. assert_nothing_raised(IOError) {f.close}
  1503. assert_nothing_raised(IOError) {f.close_read}
  1504. end
  1505. end
  1506. def test_close_read_pipe
  1507. with_pipe do |r, w|
  1508. r.close_read
  1509. assert_raise(Errno::EPIPE) { w.write "foobarbaz" }
  1510. assert_nothing_raised(IOError) {r.close_read}
  1511. assert_nothing_raised(IOError) {r.close}
  1512. assert_nothing_raised(IOError) {r.close_read}
  1513. end
  1514. end
  1515. def test_write_epipe_nosync
  1516. assert_separately([], <<-"end;")
  1517. r, w = IO.pipe
  1518. r.close
  1519. w.sync = false
  1520. assert_raise(Errno::EPIPE) {
  1521. loop { w.write "a" }
  1522. }
  1523. end;
  1524. end
  1525. def test_close_read_non_readable
  1526. with_pipe do |r, w|
  1527. assert_raise(IOError) do
  1528. w.close_read
  1529. end
  1530. end
  1531. end
  1532. def test_close_write
  1533. ruby do |f|
  1534. f.write "foobarbaz"
  1535. f.close_write
  1536. assert_equal("foobarbaz", f.read)
  1537. assert_nothing_raised(IOError) {f.close_write}
  1538. assert_nothing_raised(IOError) {f.close}
  1539. assert_nothing_raised(IOError) {f.close_write}
  1540. end
  1541. end
  1542. def test_close_write_non_readable
  1543. with_pipe do |r, w|
  1544. assert_raise(IOError) do
  1545. r.close_write
  1546. end
  1547. end
  1548. end
  1549. def test_close_read_write_separately
  1550. bug = '[ruby-list:49598]'
  1551. (1..10).each do |i|
  1552. assert_nothing_raised(IOError, "#{bug} trying ##{i}") do
  1553. IO.popen(EnvUtil.rubybin, "r+") {|f|
  1554. th = Thread.new {f.close_write}
  1555. f.close_read
  1556. th.join
  1557. }
  1558. end
  1559. end
  1560. end
  1561. def test_pid
  1562. IO.pipe {|r, w|
  1563. assert_equal(nil, r.pid)
  1564. assert_equal(nil, w.pid)
  1565. }
  1566. begin
  1567. pipe = IO.popen(EnvUtil.rubybin, "r+")
  1568. pid1 = pipe.pid
  1569. pipe.puts "p $$"
  1570. pipe.close_write
  1571. pid2 = pipe.read.chomp.to_i
  1572. assert_equal(pid2, pid1)
  1573. assert_equal(pid2, pipe.pid)
  1574. ensure
  1575. pipe.close
  1576. end
  1577. assert_raise(IOError) { pipe.pid }
  1578. end
  1579. def test_pid_after_close_read
  1580. pid1 = pid2 = nil
  1581. IO.popen("exit ;", "r+") do |io|
  1582. pid1 = io.pid
  1583. io.close_read
  1584. pid2 = io.pid
  1585. end
  1586. assert_not_nil(pid1)
  1587. assert_equal(pid1, pid2)
  1588. end
  1589. def make_tempfile
  1590. t = Tempfile.new("test_io")
  1591. t.binmode
  1592. t.puts "foo"
  1593. t.puts "bar"
  1594. t.puts "baz"
  1595. t.close
  1596. if block_given?
  1597. begin
  1598. yield t
  1599. ensure
  1600. t.close(true)
  1601. end
  1602. else
  1603. t
  1604. end
  1605. end
  1606. def test_set_lineno
  1607. make_tempfile {|t|
  1608. assert_separately(["-", t.path], <<-SRC)
  1609. open(ARGV[0]) do |f|
  1610. assert_equal(0, $.)
  1611. f.gets; assert_equal(1, $.)
  1612. f.gets; assert_equal(2, $.)
  1613. f.lineno = 1000; assert_equal(2, $.)
  1614. f.gets; assert_equal(1001, $.)
  1615. f.gets; assert_equal(1001, $.)
  1616. f.rewind; assert_equal(1001, $.)
  1617. f.gets; assert_equal(1, $.)
  1618. f.gets; assert_equal(2, $.)
  1619. f.gets; assert_equal(3, $.)
  1620. f.gets; assert_equal(3, $.)
  1621. end
  1622. SRC
  1623. }
  1624. end
  1625. def test_set_lineno_gets
  1626. pipe(proc do |w|
  1627. w.puts "foo"
  1628. w.puts "bar"
  1629. w.puts "baz"
  1630. w.close
  1631. end, proc do |r|
  1632. r.gets; assert_equal(1, $.)
  1633. r.gets; assert_equal(2, $.)
  1634. r.lineno = 1000; assert_equal(2, $.)
  1635. r.gets; assert_equal(1001, $.)
  1636. r.gets; assert_equal(1001, $.)
  1637. end)
  1638. end
  1639. def test_set_lineno_readline
  1640. pipe(proc do |w|
  1641. w.puts "foo"
  1642. w.puts "bar"
  1643. w.puts "baz"
  1644. w.close
  1645. end, proc do |r|
  1646. r.readline; assert_equal(1, $.)
  1647. r.readline; assert_equal(2, $.)
  1648. r.lineno = 1000; assert_equal(2, $.)
  1649. r.readline; assert_equal(1001, $.)
  1650. assert_raise(EOFError) { r.readline }
  1651. end)
  1652. end
  1653. def test_each_char
  1654. pipe(proc do |w|
  1655. w.puts "foo"
  1656. w.puts "bar"
  1657. w.puts "baz"
  1658. w.close
  1659. end, proc do |r|
  1660. a = []
  1661. r.each_char {|c| a << c }
  1662. assert_equal(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"], a)
  1663. end)
  1664. end
  1665. def test_lines
  1666. verbose, $VERBOSE = $VERBOSE, nil
  1667. pipe(proc do |w|
  1668. w.puts "foo"
  1669. w.puts "bar"
  1670. w.puts "baz"
  1671. w.close
  1672. end, proc do |r|
  1673. e = nil
  1674. assert_warn(/deprecated/) {
  1675. e = r.lines
  1676. }
  1677. assert_equal("foo\n", e.next)
  1678. assert_equal("bar\n", e.next)
  1679. assert_equal("baz\n", e.next)
  1680. assert_raise(StopIteration) { e.next }
  1681. end)
  1682. ensure
  1683. $VERBOSE = verbose
  1684. end
  1685. def test_bytes
  1686. verbose, $VERBOSE = $VERBOSE, nil
  1687. pipe(proc do |w|
  1688. w.binmode
  1689. w.puts "foo"
  1690. w.puts "bar"
  1691. w.puts "baz"
  1692. w.close
  1693. end, proc do |r|
  1694. e = nil
  1695. assert_warn(/deprecated/) {
  1696. e = r.bytes
  1697. }
  1698. (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
  1699. assert_equal(c.ord, e.next)
  1700. end
  1701. assert_raise(StopIteration) { e.next }
  1702. end)
  1703. ensure
  1704. $VERBOSE = verbose
  1705. end
  1706. def test_chars
  1707. verbose, $VERBOSE = $VERBOSE, nil
  1708. pipe(proc do |w|
  1709. w.puts "foo"
  1710. w.puts "bar"
  1711. w.puts "baz"
  1712. w.close
  1713. end, proc do |r|
  1714. e = nil
  1715. assert_warn(/deprecated/) {
  1716. e = r.chars
  1717. }
  1718. (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
  1719. assert_equal(c, e.next)
  1720. end
  1721. assert_raise(StopIteration) { e.next }
  1722. end)
  1723. ensure
  1724. $VERBOSE = verbose
  1725. end
  1726. def test_readbyte
  1727. pipe(proc do |w|
  1728. w.binmode
  1729. w.puts "foo"
  1730. w.puts "bar"
  1731. w.puts "baz"
  1732. w.close
  1733. end, proc do |r|
  1734. r.binmode
  1735. (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
  1736. assert_equal(c.ord, r.readbyte)
  1737. end
  1738. assert_raise(EOFError) { r.readbyte }
  1739. end)
  1740. end
  1741. def test_readchar
  1742. pipe(proc do |w|
  1743. w.puts "foo"
  1744. w.puts "bar"
  1745. w.puts "baz"
  1746. w.close
  1747. end, proc do |r|
  1748. (%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
  1749. assert_equal(c, r.readchar)
  1750. end
  1751. assert_raise(EOFError) { r.readchar }
  1752. end)
  1753. end
  1754. def test_close_on_exec
  1755. ruby do |f|
  1756. assert_equal(true, f.close_on_exec?)
  1757. f.close_on_exec = false
  1758. assert_equal(false, f.close_on_exec?)
  1759. f.close_on_exec = true
  1760. assert_equal(true, f.close_on_exec?)
  1761. f.close_on_exec = false
  1762. assert_equal(false, f.close_on_exec?)
  1763. end
  1764. with_pipe do |r, w|
  1765. assert_equal(true, r.close_on_exec?)
  1766. r.close_on_exec = false
  1767. assert_equal(false, r.close_on_exec?)
  1768. r.close_on_exec = true
  1769. assert_equal(true, r.close_on_exec?)
  1770. r.close_on_exec = false
  1771. assert_equal(false, r.close_on_exec?)
  1772. assert_equal(true, w.close_on_exec?)
  1773. w.close_on_exec = false
  1774. assert_equal(false, w.close_on_exec?)
  1775. w.close_on_exec = true
  1776. assert_equal(true, w.close_on_exec?)
  1777. w.close_on_exec = false
  1778. assert_equal(false, w.close_on_exec?)
  1779. end
  1780. end if have_close_on_exec?
  1781. def test_pos
  1782. make_tempfile {|t|
  1783. open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
  1784. f.write "Hello"
  1785. assert_equal(5, f.pos)
  1786. end
  1787. open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
  1788. f.sync = true
  1789. f.read
  1790. f.write "Hello"
  1791. assert_equal(5, f.pos)
  1792. end
  1793. }
  1794. end
  1795. def test_pos_with_getc
  1796. _bug6179 = '[ruby-core:43497]'
  1797. make_tempfile {|t|
  1798. ["", "t", "b"].each do |mode|
  1799. open(t.path, "w#{mode}") do |f|
  1800. f.write "0123456789\n"
  1801. end
  1802. open(t.path, "r#{mode}") do |f|
  1803. assert_equal 0, f.pos, "mode=r#{mode}"
  1804. assert_equal '0', f.getc, "mode=r#{mode}"
  1805. assert_equal 1, f.pos, "mode=r#{mode}"
  1806. assert_equal '1', f.getc, "mode=r#{mode}"
  1807. assert_equal 2, f.pos, "mode=r#{mode}"
  1808. assert_equal '2', f.getc, "mode=r#{mode}"
  1809. assert_equal 3, f.pos, "mode=r#{mode}"
  1810. assert_equal '3', f.getc, "mode=r#{mode}"
  1811. assert_equal 4, f.pos, "mode=r#{mode}"
  1812. assert_equal '4', f.getc, "mode=r#{mode}"
  1813. end
  1814. end
  1815. }
  1816. end
  1817. def can_seek_data(f)
  1818. if /linux/ =~ RUBY_PLATFORM
  1819. require "-test-/file"
  1820. # lseek(2)
  1821. case Bug::File::Fs.fsname(f.path)
  1822. when "btrfs"
  1823. return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,1]) >= 0
  1824. when "ocfs"
  1825. return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,2]) >= 0
  1826. when "xfs"
  1827. return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,5]) >= 0
  1828. when "ext4"
  1829. return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
  1830. when "tmpfs"
  1831. return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
  1832. end
  1833. end
  1834. false
  1835. end
  1836. def test_seek
  1837. make_tempfile {|t|
  1838. open(t.path) { |f|
  1839. f.seek(9)
  1840. assert_equal("az\n", f.read)
  1841. }
  1842. open(t.path) { |f|
  1843. f.seek(9, IO::SEEK_SET)
  1844. assert_equal("az\n", f.read)
  1845. }
  1846. open(t.path) { |f|
  1847. f.seek(-4, IO::SEEK_END)
  1848. assert_equal("baz\n", f.read)
  1849. }
  1850. open(t.path) { |f|
  1851. assert_equal("foo\n", f.gets)
  1852. f.seek(2, IO::SEEK_CUR)
  1853. assert_equal("r\nbaz\n", f.read)
  1854. }
  1855. if defined?(IO::SEEK_DATA)
  1856. open(t.path) { |f|
  1857. break unless can_seek_data(f)
  1858. assert_equal("foo\n", f.gets)
  1859. f.seek(0, IO::SEEK_DATA)
  1860. assert_equal("foo\nbar\nbaz\n", f.read)
  1861. }
  1862. open(t.path, 'r+') { |f|
  1863. break unless can_seek_data(f)
  1864. f.seek(100*1024, IO::SEEK_SET)
  1865. f.print("zot\n")
  1866. f.seek(50*1024, IO::SEEK_DATA)
  1867. assert_operator(f.pos, :>=, 50*1024)
  1868. assert_match(/\A\0*zot\n\z/, f.read)
  1869. }
  1870. end
  1871. if defined?(IO::SEEK_HOLE)
  1872. open(t.path) { |f|
  1873. break unless can_seek_data(f)
  1874. assert_equal("foo\n", f.gets)
  1875. f.seek(0, IO::SEEK_HOLE)
  1876. assert_operator(f.pos, :>, 20)
  1877. f.seek(100*1024, IO::SEEK_HOLE)
  1878. assert_equal("", f.read)
  1879. }
  1880. end
  1881. }
  1882. end
  1883. def test_seek_symwhence
  1884. make_tempfile {|t|
  1885. open(t.path) { |f|
  1886. f.seek(9, :SET)
  1887. assert_equal("az\n", f.read)
  1888. }
  1889. open(t.path) { |f|
  1890. f.seek(-4, :END)
  1891. assert_equal("baz\n", f.read)
  1892. }
  1893. open(t.path) { |f|
  1894. assert_equal("foo\n", f.gets)
  1895. f.seek(2, :CUR)
  1896. assert_equal("r\nbaz\n", f.read)
  1897. }
  1898. if defined?(IO::SEEK_DATA)
  1899. open(t.path) { |f|
  1900. break unless can_seek_data(f)
  1901. assert_equal("foo\n", f.gets)
  1902. f.seek(0, :DATA)
  1903. assert_equal("foo\nbar\nbaz\n", f.read)
  1904. }
  1905. open(t.path, 'r+') { |f|
  1906. break unless can_seek_data(f)
  1907. f.seek(100*1024, :SET)
  1908. f.print("zot\n")
  1909. f.seek(50*1024, :DATA)
  1910. assert_operator(f.pos, :>=, 50*1024)
  1911. assert_match(/\A\0*zot\n\z/, f.read)
  1912. }
  1913. end
  1914. if defined?(IO::SEEK_HOLE)
  1915. open(t.path) { |f|
  1916. break unless can_seek_data(f)
  1917. assert_equal("foo\n", f.gets)
  1918. f.seek(0, :HOLE)
  1919. assert_operator(f.pos, :>, 20)
  1920. f.seek(100*1024, :HOLE)
  1921. assert_equal("", f.read)
  1922. }
  1923. end
  1924. }
  1925. end
  1926. def test_sysseek
  1927. make_tempfile {|t|
  1928. open(t.path) do |f|
  1929. f.sysseek(-4, IO::SEEK_END)
  1930. assert_equal("baz\n", f.read)
  1931. end
  1932. open(t.path) do |f|
  1933. a = [f.getc, f.getc, f.getc]
  1934. a.reverse_each {|c| f.ungetc c }
  1935. assert_raise(IOError) { f.sysseek(1) }
  1936. end
  1937. }
  1938. end
  1939. def test_syswrite
  1940. make_tempfile {|t|
  1941. open(t.path, "w") do |f|
  1942. o = Object.new
  1943. def o.to_s; "FOO\n"; end
  1944. f.syswrite(o)
  1945. end
  1946. assert_equal("FOO\n", File.read(t.path))
  1947. }
  1948. end
  1949. def test_sysread
  1950. make_tempfile {|t|
  1951. open(t.path) do |f|
  1952. a = [f.getc, f.getc, f.getc]
  1953. a.reverse_each {|c| f.ungetc c }
  1954. assert_raise(IOError) { f.sysread(1) }
  1955. end
  1956. }
  1957. end
  1958. def test_sysread_with_not_empty_buffer
  1959. pipe(proc do |w|
  1960. w.write "foob"
  1961. w.close
  1962. end, proc do |r|
  1963. r.sysread( 5, s = "01234567" )
  1964. assert_equal( "foob", s )
  1965. end)
  1966. end
  1967. def test_flag
  1968. make_tempfile {|t|
  1969. assert_raise(ArgumentError) do
  1970. open(t.path, "z") { }
  1971. end
  1972. assert_raise(ArgumentError) do
  1973. open(t.path, "rr") { }
  1974. end
  1975. assert_raise(ArgumentError) do
  1976. open(t.path, "rbt") { }
  1977. end
  1978. }
  1979. end
  1980. def test_sysopen
  1981. make_tempfile {|t|
  1982. fd = IO.sysopen(t.path)
  1983. assert_kind_of(Integer, fd)
  1984. f = IO.for_fd(fd)
  1985. assert_equal("foo\nbar\nbaz\n", f.read)
  1986. f.close
  1987. fd = IO.sysopen(t.path, "w", 0666)
  1988. assert_kind_of(Integer, fd)
  1989. if defined?(Fcntl::F_GETFL)
  1990. f = IO.for_fd(fd)
  1991. else
  1992. f = IO.for_fd(fd, 0666)
  1993. end
  1994. f.write("FOO\n")
  1995. f.close
  1996. fd = IO.sysopen(t.path, "r")
  1997. assert_kind_of(Integer, fd)
  1998. f = IO.for_fd(fd)
  1999. assert_equal("FOO\n", f.read)
  2000. f.close
  2001. }
  2002. end
  2003. def try_fdopen(fd, autoclose = true, level = 50)
  2004. if level > 0
  2005. begin
  2006. 1.times {return try_fdopen(fd, autoclose, level - 1)}
  2007. ensure
  2008. GC.start
  2009. end
  2010. else
  2011. WeakRef.new(IO.for_fd(fd, autoclose: autoclose))
  2012. end
  2013. end
  2014. def test_autoclose
  2015. feature2250 = '[ruby-core:26222]'
  2016. pre = 'ft2250'
  2017. Dir.mktmpdir {|d|
  2018. t = open("#{d}/#{pre}", "w")
  2019. f = IO.for_fd(t.fileno)
  2020. assert_equal(true, f.autoclose?)
  2021. f.autoclose = false
  2022. assert_equal(false, f.autoclose?)
  2023. f.close
  2024. assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
  2025. t = open("#{d}/#{pre}", "w")
  2026. f = IO.for_fd(t.fileno, autoclose: false)
  2027. assert_equal(false, f.autoclose?)
  2028. f.autoclose = true
  2029. assert_equal(true, f.autoclose?)
  2030. f.close
  2031. assert_raise(Errno::EBADF, feature2250) {t.close}
  2032. }
  2033. end
  2034. def test_autoclose_true_closed_by_finalizer
  2035. # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1465760
  2036. # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1469765
  2037. skip 'this randomly fails with MJIT' if RubyVM::MJIT.enabled?
  2038. feature2250 = '[ruby-core:26222]'
  2039. pre = 'ft2250'
  2040. t = Tempfile.new(pre)
  2041. w = try_fdopen(t.fileno)
  2042. begin
  2043. w.close
  2044. begin
  2045. t.close
  2046. rescue Errno::EBADF
  2047. end
  2048. skip "expect IO object was GC'ed but not recycled yet"
  2049. rescue WeakRef::RefError
  2050. assert_raise(Errno::EBADF, feature2250) {t.close}
  2051. end
  2052. ensure
  2053. t&.close!
  2054. end
  2055. def test_autoclose_false_closed_by_finalizer
  2056. feature2250 = '[ruby-core:26222]'
  2057. pre = 'ft2250'
  2058. t = Tempfile.new(pre)
  2059. w = try_fdopen(t.fileno, false)
  2060. begin
  2061. w.close
  2062. t.close
  2063. skip "expect IO object was GC'ed but not recycled yet"
  2064. rescue WeakRef::RefError
  2065. assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
  2066. end
  2067. ensure
  2068. t.close!
  2069. end
  2070. def test_open_redirect
  2071. o = Object.new
  2072. def o.to_open; self; end
  2073. assert_equal(o, open(o))
  2074. o2 = nil
  2075. open(o) do |f|
  2076. o2 = f
  2077. end
  2078. assert_equal(o, o2)
  2079. end
  2080. def test_open_redirect_keyword
  2081. o = Object.new
  2082. def o.to_open(**kw); kw; end
  2083. assert_equal({:a=>1}, open(o, a: 1))
  2084. assert_raise(ArgumentError) { open(o, {a: 1}) }
  2085. class << o
  2086. remove_method(:to_open)
  2087. end
  2088. def o.to_open(kw); kw; end
  2089. assert_equal({:a=>1}, open(o, a: 1))
  2090. assert_equal({:a=>1}, open(o, {a: 1}))
  2091. end
  2092. def test_open_pipe
  2093. open("|" + EnvUtil.rubybin, "r+") do |f|
  2094. f.puts "puts 'foo'"
  2095. f.close_write
  2096. assert_equal("foo\n", f.read)
  2097. end
  2098. end
  2099. def test_read_command
  2100. assert_equal("foo\n", IO.read("|echo foo"))
  2101. assert_raise(Errno::ENOENT, Errno::EINVAL) do
  2102. File.read("|#{EnvUtil.rubybin} -e puts")
  2103. end
  2104. assert_raise(Errno::ENOENT, Errno::EINVAL) do
  2105. File.binread("|#{EnvUtil.rubybin} -e puts")
  2106. end
  2107. assert_raise(Errno::ENOENT, Errno::EINVAL) do
  2108. Class.new(IO).read("|#{EnvUtil.rubybin} -e puts")
  2109. end
  2110. assert_raise(Errno::ENOENT, Errno::EINVAL) do
  2111. Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
  2112. end
  2113. assert_raise(Errno::ESPIPE) do
  2114. IO.read("|echo foo", 1, 1)
  2115. end
  2116. end
  2117. def test_reopen
  2118. make_tempfile {|t|
  2119. open(__FILE__) do |f|
  2120. f.gets
  2121. assert_nothing_raised {
  2122. f.reopen(t.path)
  2123. assert_equal("foo\n", f.gets)
  2124. }
  2125. end
  2126. open(__FILE__) do |f|
  2127. f.gets
  2128. f2 = open(t.path)
  2129. begin
  2130. f2.gets
  2131. assert_nothing_raised {
  2132. f.reopen(f2)
  2133. assert_equal("bar\n", f.gets, '[ruby-core:24240]')
  2134. }
  2135. ensure
  2136. f2.close
  2137. end
  2138. end
  2139. open(__FILE__) do |f|
  2140. f2 = open(t.path)
  2141. begin
  2142. f.reopen(f2)
  2143. assert_equal("foo\n", f.gets)
  2144. assert_equal("bar\n", f.gets)
  2145. f.reopen(f2)
  2146. assert_equal("baz\n", f.gets, '[ruby-dev:39479]')
  2147. ensure
  2148. f2.close
  2149. end
  2150. end
  2151. }
  2152. end
  2153. def test_reopen_inherit
  2154. mkcdtmpdir {
  2155. system(EnvUtil.rubybin, '-e', <<-"End")
  2156. f = open("out", "w")
  2157. STDOUT.reopen(f)
  2158. STDERR.reopen(f)
  2159. system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"')
  2160. system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"')
  2161. End
  2162. assert_equal("outerr", File.read("out"))
  2163. }
  2164. end
  2165. def test_reopen_stdio
  2166. mkcdtmpdir {
  2167. fname = 'bug11319'
  2168. File.write(fname, 'hello')
  2169. system(EnvUtil.rubybin, '-e', "STDOUT.reopen('#{fname}', 'w+')")
  2170. assert_equal('', File.read(fname))
  2171. }
  2172. end
  2173. def test_reopen_mode
  2174. feature7067 = '[ruby-core:47694]'
  2175. make_tempfile {|t|
  2176. open(__FILE__) do |f|
  2177. assert_nothing_raised {
  2178. f.reopen(t.path, "r")
  2179. assert_equal("foo\n", f.gets)
  2180. }
  2181. end
  2182. open(__FILE__) do |f|
  2183. assert_nothing_raised(feature7067) {
  2184. f.reopen(t.path, File::RDONLY)
  2185. assert_equal("foo\n", f.gets)
  2186. }
  2187. end
  2188. }
  2189. end
  2190. def test_reopen_opt
  2191. feature7103 = '[ruby-core:47806]'
  2192. make_tempfile {|t|
  2193. open(__FILE__) do |f|
  2194. assert_nothing_raised(feature7103) {
  2195. f.reopen(t.path, "r", binmode: true)
  2196. }
  2197. assert_equal("foo\n", f.gets)
  2198. end
  2199. open(__FILE__) do |f|
  2200. assert_nothing_raised(feature7103) {
  2201. f.reopen(t.path, autoclose: false)
  2202. }
  2203. assert_equal("foo\n", f.gets)
  2204. end
  2205. }
  2206. end
  2207. def make_tempfile_for_encoding
  2208. t = make_tempfile
  2209. open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"}
  2210. if block_given?
  2211. yield t
  2212. else
  2213. t
  2214. end
  2215. ensure
  2216. t&.close(true) if block_given?
  2217. end
  2218. def test_reopen_encoding
  2219. make_tempfile_for_encoding {|t|
  2220. open(__FILE__) {|f|
  2221. f.reopen(t.path, "r:utf-8")
  2222. s = f.gets
  2223. assert_equal(Encoding::UTF_8, s.encoding)
  2224. assert_equal("\u7d05\u7389bar\n", s)
  2225. }
  2226. open(__FILE__) {|f|
  2227. f.reopen(t.path, "r:UTF-8:EUC-JP")
  2228. s = f.gets
  2229. assert_equal(Encoding::EUC_JP, s.encoding)
  2230. assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
  2231. }
  2232. }
  2233. end
  2234. def test_reopen_opt_encoding
  2235. feature7103 = '[ruby-core:47806]'
  2236. make_tempfile_for_encoding {|t|
  2237. open(__FILE__) {|f|
  2238. assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "ASCII-8BIT")}
  2239. s = f.gets
  2240. assert_equal(Encoding::ASCII_8BIT, s.encoding)
  2241. assert_equal("\xe7\xb4\x85\xe7\x8e\x89bar\n", s)
  2242. }
  2243. open(__FILE__) {|f|
  2244. assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "UTF-8:EUC-JP")}
  2245. s = f.gets
  2246. assert_equal(Encoding::EUC_JP, s.encoding)
  2247. assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
  2248. }
  2249. }
  2250. end
  2251. bug11320 = '[ruby-core:69780] [Bug #11320]'
  2252. ["UTF-8", "EUC-JP", "Shift_JIS"].each do |enc|
  2253. define_method("test_reopen_nonascii(#{enc})") do
  2254. mkcdtmpdir do
  2255. fname = "\u{30eb 30d3 30fc}".encode(enc)
  2256. File.write(fname, '')
  2257. assert_file.exist?(fname)
  2258. stdin = $stdin.dup
  2259. begin
  2260. assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") {
  2261. $stdin.reopen(fname, 'r')
  2262. }
  2263. ensure
  2264. $stdin.reopen(stdin)
  2265. stdin.close
  2266. end
  2267. end
  2268. end
  2269. end
  2270. def test_foreach
  2271. a = []
  2272. IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
  2273. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  2274. a = []
  2275. IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
  2276. assert_equal(["zot\n"], a)
  2277. make_tempfile {|t|
  2278. a = []
  2279. IO.foreach(t.path) {|x| a << x }
  2280. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  2281. a = []
  2282. IO.foreach(t.path, :mode => "r") {|x| a << x }
  2283. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  2284. a = []
  2285. IO.foreach(t.path, :open_args => []) {|x| a << x }
  2286. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  2287. a = []
  2288. IO.foreach(t.path, :open_args => ["r"]) {|x| a << x }
  2289. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  2290. a = []
  2291. IO.foreach(t.path, "b") {|x| a << x }
  2292. assert_equal(["foo\nb", "ar\nb", "az\n"], a)
  2293. a = []
  2294. IO.foreach(t.path, 3) {|x| a << x }
  2295. assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a)
  2296. a = []
  2297. IO.foreach(t.path, "b", 3) {|x| a << x }
  2298. assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a)
  2299. bug = '[ruby-dev:31525]'
  2300. assert_raise(ArgumentError, bug) {IO.foreach}
  2301. a = nil
  2302. assert_nothing_raised(ArgumentError, bug) {a = IO.foreach(t.path).to_a}
  2303. assert_equal(["foo\n", "bar\n", "baz\n"], a, bug)
  2304. bug6054 = '[ruby-dev:45267]'
  2305. assert_raise_with_message(IOError, /not opened for reading/, bug6054) do
  2306. IO.foreach(t.path, mode:"w").next
  2307. end
  2308. }
  2309. end
  2310. def test_s_readlines
  2311. make_tempfile {|t|
  2312. assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
  2313. assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b"))
  2314. assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2))
  2315. assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2))
  2316. }
  2317. end
  2318. def test_printf
  2319. pipe(proc do |w|
  2320. printf(w, "foo %s baz\n", "bar")
  2321. w.close_write
  2322. end, proc do |r|
  2323. assert_equal("foo bar baz\n", r.read)
  2324. end)
  2325. end
  2326. def test_print
  2327. make_tempfile {|t|
  2328. assert_in_out_err(["-", t.path],
  2329. "print while $<.gets",
  2330. %w(foo bar baz), [])
  2331. }
  2332. end
  2333. def test_print_separators
  2334. EnvUtil.suppress_warning {
  2335. $, = ':'
  2336. $\ = "\n"
  2337. }
  2338. pipe(proc do |w|
  2339. w.print('a')
  2340. EnvUtil.suppress_warning {w.print('a','b','c')}
  2341. w.close
  2342. end, proc do |r|
  2343. assert_equal("a\n", r.gets)
  2344. assert_equal("a:b:c\n", r.gets)
  2345. assert_nil r.gets
  2346. r.close
  2347. end)
  2348. ensure
  2349. $, = nil
  2350. $\ = nil
  2351. end
  2352. def test_putc
  2353. pipe(proc do |w|
  2354. w.putc "A"
  2355. w.putc "BC"
  2356. w.putc 68
  2357. w.close_write
  2358. end, proc do |r|
  2359. assert_equal("ABD", r.read)
  2360. end)
  2361. assert_in_out_err([], "putc 65", %w(A), [])
  2362. end
  2363. def test_puts_recursive_array
  2364. a = ["foo"]
  2365. a << a
  2366. pipe(proc do |w|
  2367. w.puts a
  2368. w.close
  2369. end, proc do |r|
  2370. assert_equal("foo\n[...]\n", r.read)
  2371. end)
  2372. end
  2373. def test_puts_parallel
  2374. skip "not portable"
  2375. pipe(proc do |w|
  2376. threads = []
  2377. 100.times do
  2378. threads << Thread.new { w.puts "hey" }
  2379. end
  2380. threads.each(&:join)
  2381. w.close
  2382. end, proc do |r|
  2383. assert_equal("hey\n" * 100, r.read)
  2384. end)
  2385. end
  2386. def test_puts_old_write
  2387. capture = String.new
  2388. def capture.write(str)
  2389. self << str
  2390. end
  2391. capture.clear
  2392. assert_warning(/[.#]write is outdated/) do
  2393. stdout, $stdout = $stdout, capture
  2394. puts "hey"
  2395. ensure
  2396. $stdout = stdout
  2397. end
  2398. assert_equal("hey\n", capture)
  2399. end
  2400. def test_display
  2401. pipe(proc do |w|
  2402. "foo".display(w)
  2403. w.close
  2404. end, proc do |r|
  2405. assert_equal("foo", r.read)
  2406. end)
  2407. assert_in_out_err([], "'foo'.display", %w(foo), [])
  2408. end
  2409. def test_set_stdout
  2410. assert_raise(TypeError) { $> = Object.new }
  2411. assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
  2412. assert_separately(%w[-Eutf-8], "#{<<~"begin;"}\n#{<<~"end;"}")
  2413. begin;
  2414. alias $\u{6a19 6e96 51fa 529b} $stdout
  2415. x = eval("class X\u{307b 3052}; self; end".encode("euc-jp"))
  2416. assert_raise_with_message(TypeError, /\\$\u{6a19 6e96 51fa 529b} must.*, X\u{307b 3052} given/) do
  2417. $\u{6a19 6e96 51fa 529b} = x.new
  2418. end
  2419. end;
  2420. end
  2421. def test_initialize
  2422. return unless defined?(Fcntl::F_GETFL)
  2423. make_tempfile {|t|
  2424. fd = IO.sysopen(t.path, "w")
  2425. assert_kind_of(Integer, fd)
  2426. %w[r r+ w+ a+].each do |mode|
  2427. assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
  2428. end
  2429. f = IO.new(fd, "w")
  2430. f.write("FOO\n")
  2431. f.close
  2432. assert_equal("FOO\n", File.read(t.path))
  2433. }
  2434. end
  2435. def test_reinitialize
  2436. make_tempfile {|t|
  2437. f = open(t.path)
  2438. begin
  2439. assert_raise(RuntimeError) do
  2440. f.instance_eval { initialize }
  2441. end
  2442. ensure
  2443. f.close
  2444. end
  2445. }
  2446. end
  2447. def test_new_with_block
  2448. assert_in_out_err([], "r, w = IO.pipe; r.autoclose=false; IO.new(r.fileno) {}.close", [], /^.+$/)
  2449. n = "IO\u{5165 51fa 529b}"
  2450. c = eval("class #{n} < IO; self; end")
  2451. IO.pipe do |r, w|
  2452. assert_warning(/#{n}/) {
  2453. r.autoclose=false
  2454. io = c.new(r.fileno) {}
  2455. io.close
  2456. }
  2457. end
  2458. end
  2459. def test_readline2
  2460. assert_in_out_err(["-e", <<-SRC], "foo\nbar\nbaz\n", %w(foo bar baz end), [])
  2461. puts readline
  2462. puts readline
  2463. puts readline
  2464. begin
  2465. puts readline
  2466. rescue EOFError
  2467. puts "end"
  2468. end
  2469. SRC
  2470. end
  2471. def test_readlines
  2472. assert_in_out_err(["-e", "p readlines"], "foo\nbar\nbaz\n",
  2473. ["[\"foo\\n\", \"bar\\n\", \"baz\\n\"]"], [])
  2474. end
  2475. def test_s_read
  2476. make_tempfile {|t|
  2477. assert_equal("foo\nbar\nbaz\n", File.read(t.path))
  2478. assert_equal("foo\nba", File.read(t.path, 6))
  2479. assert_equal("bar\n", File.read(t.path, 4, 4))
  2480. }
  2481. end
  2482. def test_uninitialized
  2483. assert_raise(IOError) { IO.allocate.print "" }
  2484. end
  2485. def test_nofollow
  2486. # O_NOFOLLOW is not standard.
  2487. mkcdtmpdir {
  2488. open("file", "w") {|f| f << "content" }
  2489. begin
  2490. File.symlink("file", "slnk")
  2491. rescue NotImplementedError
  2492. return
  2493. end
  2494. assert_raise(Errno::EMLINK, Errno::ELOOP) {
  2495. open("slnk", File::RDONLY|File::NOFOLLOW) {}
  2496. }
  2497. assert_raise(Errno::EMLINK, Errno::ELOOP) {
  2498. File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {}
  2499. }
  2500. }
  2501. end if /freebsd|linux/ =~ RUBY_PLATFORM and defined? File::NOFOLLOW
  2502. def test_binmode_after_closed
  2503. make_tempfile {|t|
  2504. assert_raise(IOError) {t.binmode}
  2505. }
  2506. end
  2507. def test_DATA_binmode
  2508. assert_separately([], <<-SRC)
  2509. assert_not_predicate(DATA, :binmode?)
  2510. __END__
  2511. SRC
  2512. end
  2513. def test_threaded_flush
  2514. bug3585 = '[ruby-core:31348]'
  2515. src = "#{<<~"begin;"}\n#{<<~'end;'}"
  2516. begin;
  2517. t = Thread.new { sleep 3 }
  2518. Thread.new {sleep 1; t.kill; p 'hi!'}
  2519. t.join
  2520. end;
  2521. 10.times.map do
  2522. Thread.start do
  2523. assert_in_out_err([], src, timeout: 20) {|stdout, stderr|
  2524. assert_no_match(/hi.*hi/, stderr.join, bug3585)
  2525. }
  2526. end
  2527. end.each {|th| th.join}
  2528. end
  2529. def test_flush_in_finalizer1
  2530. bug3910 = '[ruby-dev:42341]'
  2531. tmp = Tempfile.open("bug3910") {|t|
  2532. path = t.path
  2533. t.close
  2534. fds = []
  2535. assert_nothing_raised(TypeError, bug3910) do
  2536. 500.times {
  2537. f = File.open(path, "w")
  2538. f.instance_variable_set(:@test_flush_in_finalizer1, true)
  2539. fds << f.fileno
  2540. f.print "hoge"
  2541. }
  2542. end
  2543. t
  2544. }
  2545. ensure
  2546. ObjectSpace.each_object(File) {|f|
  2547. if f.instance_variables.include?(:@test_flush_in_finalizer1)
  2548. f.close
  2549. end
  2550. }
  2551. tmp.close!
  2552. end
  2553. def test_flush_in_finalizer2
  2554. bug3910 = '[ruby-dev:42341]'
  2555. Tempfile.open("bug3910") {|t|
  2556. path = t.path
  2557. t.close
  2558. begin
  2559. 1.times do
  2560. io = open(path,"w")
  2561. io.instance_variable_set(:@test_flush_in_finalizer2, true)
  2562. io.print "hoge"
  2563. end
  2564. assert_nothing_raised(TypeError, bug3910) do
  2565. GC.start
  2566. end
  2567. ensure
  2568. ObjectSpace.each_object(File) {|f|
  2569. if f.instance_variables.include?(:@test_flush_in_finalizer2)
  2570. f.close
  2571. end
  2572. }
  2573. end
  2574. t.close!
  2575. }
  2576. end
  2577. def test_readlines_limit_0
  2578. bug4024 = '[ruby-dev:42538]'
  2579. make_tempfile {|t|
  2580. open(t.path, "r") do |io|
  2581. assert_raise(ArgumentError, bug4024) do
  2582. io.readlines(0)
  2583. end
  2584. end
  2585. }
  2586. end
  2587. def test_each_line_limit_0
  2588. bug4024 = '[ruby-dev:42538]'
  2589. make_tempfile {|t|
  2590. open(t.path, "r") do |io|
  2591. assert_raise(ArgumentError, bug4024) do
  2592. io.each_line(0).next
  2593. end
  2594. end
  2595. }
  2596. end
  2597. def os_and_fs(path)
  2598. uname = Etc.uname
  2599. os = "#{uname[:sysname]} #{uname[:release]}"
  2600. fs = nil
  2601. if uname[:sysname] == 'Linux'
  2602. # [ruby-dev:45703] Old Linux's fadvise() doesn't work on tmpfs.
  2603. mount = `mount`
  2604. mountpoints = []
  2605. mount.scan(/ on (\S+) type (\S+) /) {
  2606. mountpoints << [$1, $2]
  2607. }
  2608. mountpoints.sort_by {|mountpoint, fstype| mountpoint.length }.reverse_each {|mountpoint, fstype|
  2609. if path == mountpoint
  2610. fs = fstype
  2611. break
  2612. end
  2613. mountpoint += "/" if %r{/\z} !~ mountpoint
  2614. if path.start_with?(mountpoint)
  2615. fs = fstype
  2616. break
  2617. end
  2618. }
  2619. end
  2620. if fs
  2621. "#{fs} on #{os}"
  2622. else
  2623. os
  2624. end
  2625. end
  2626. def test_advise
  2627. make_tempfile {|tf|
  2628. assert_raise(ArgumentError, "no arguments") { tf.advise }
  2629. %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv|
  2630. [[0,0], [0, 20], [400, 2]].each do |offset, len|
  2631. open(tf.path) do |t|
  2632. ret = assert_nothing_raised(lambda { os_and_fs(tf.path) }) {
  2633. begin
  2634. t.advise(adv, offset, len)
  2635. rescue Errno::EINVAL => e
  2636. if /linux/ =~ RUBY_PLATFORM && (Etc.uname[:release].split('.').map(&:to_i) <=> [3,6]) < 0
  2637. next # [ruby-core:65355] tmpfs is not supported
  2638. else
  2639. raise e
  2640. end
  2641. end
  2642. }
  2643. assert_nil(ret)
  2644. assert_raise(ArgumentError, "superfluous arguments") do
  2645. t.advise(adv, offset, len, offset)
  2646. end
  2647. assert_raise(TypeError, "wrong type for first argument") do
  2648. t.advise(adv.to_s, offset, len)
  2649. end
  2650. assert_raise(TypeError, "wrong type for last argument") do
  2651. t.advise(adv, offset, Array(len))
  2652. end
  2653. assert_raise(RangeError, "last argument too big") do
  2654. t.advise(adv, offset, 9999e99)
  2655. end
  2656. end
  2657. assert_raise(IOError, "closed file") do
  2658. make_tempfile {|tf2|
  2659. tf2.advise(adv.to_sym, offset, len)
  2660. }
  2661. end
  2662. end
  2663. end
  2664. }
  2665. end
  2666. def test_invalid_advise
  2667. feature4204 = '[ruby-dev:42887]'
  2668. make_tempfile {|tf|
  2669. %W{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv|
  2670. [[0,0], [0, 20], [400, 2]].each do |offset, len|
  2671. open(tf.path) do |t|
  2672. assert_raise_with_message(NotImplementedError, /#{Regexp.quote(adv.inspect)}/, feature4204) { t.advise(adv, offset, len) }
  2673. end
  2674. end
  2675. end
  2676. }
  2677. end
  2678. def test_fcntl_lock_linux
  2679. pad = 0
  2680. Tempfile.create(self.class.name) do |f|
  2681. r, w = IO.pipe
  2682. pid = fork do
  2683. r.close
  2684. lock = [Fcntl::F_WRLCK, IO::SEEK_SET, pad, 12, 34, 0].pack("s!s!i!L!L!i!")
  2685. f.fcntl Fcntl::F_SETLKW, lock
  2686. w.syswrite "."
  2687. sleep
  2688. end
  2689. w.close
  2690. assert_equal ".", r.read(1)
  2691. r.close
  2692. pad = 0
  2693. getlock = [Fcntl::F_WRLCK, 0, pad, 0, 0, 0].pack("s!s!i!L!L!i!")
  2694. f.fcntl Fcntl::F_GETLK, getlock
  2695. ptype, whence, pad, start, len, lockpid = getlock.unpack("s!s!i!L!L!i!")
  2696. assert_equal(ptype, Fcntl::F_WRLCK)
  2697. assert_equal(whence, IO::SEEK_SET)
  2698. assert_equal(start, 12)
  2699. assert_equal(len, 34)
  2700. assert_equal(pid, lockpid)
  2701. Process.kill :TERM, pid
  2702. Process.waitpid2(pid)
  2703. end
  2704. end if /x86_64-linux/ =~ RUBY_PLATFORM and # A binary form of struct flock depend on platform
  2705. [nil].pack("p").bytesize == 8 # unless x32 platform.
  2706. def test_fcntl_lock_freebsd
  2707. start = 12
  2708. len = 34
  2709. sysid = 0
  2710. Tempfile.create(self.class.name) do |f|
  2711. r, w = IO.pipe
  2712. pid = fork do
  2713. r.close
  2714. lock = [start, len, 0, Fcntl::F_WRLCK, IO::SEEK_SET, sysid].pack("qqis!s!i!")
  2715. f.fcntl Fcntl::F_SETLKW, lock
  2716. w.syswrite "."
  2717. sleep
  2718. end
  2719. w.close
  2720. assert_equal ".", r.read(1)
  2721. r.close
  2722. getlock = [0, 0, 0, Fcntl::F_WRLCK, 0, 0].pack("qqis!s!i!")
  2723. f.fcntl Fcntl::F_GETLK, getlock
  2724. start, len, lockpid, ptype, whence, sysid = getlock.unpack("qqis!s!i!")
  2725. assert_equal(ptype, Fcntl::F_WRLCK)
  2726. assert_equal(whence, IO::SEEK_SET)
  2727. assert_equal(start, 12)
  2728. assert_equal(len, 34)
  2729. assert_equal(pid, lockpid)
  2730. Process.kill :TERM, pid
  2731. Process.waitpid2(pid)
  2732. end
  2733. end if /freebsd/ =~ RUBY_PLATFORM # A binary form of struct flock depend on platform
  2734. def test_fcntl_dupfd
  2735. Tempfile.create(self.class.name) do |f|
  2736. fd = f.fcntl(Fcntl::F_DUPFD, 63)
  2737. begin
  2738. assert_operator(fd, :>=, 63)
  2739. ensure
  2740. IO.for_fd(fd).close
  2741. end
  2742. end
  2743. end
  2744. def test_cross_thread_close_fd
  2745. with_pipe do |r,w|
  2746. read_thread = Thread.new do
  2747. begin
  2748. r.read(1)
  2749. rescue => e
  2750. e
  2751. end
  2752. end
  2753. sleep(0.1) until read_thread.stop?
  2754. r.close
  2755. read_thread.join
  2756. assert_kind_of(IOError, read_thread.value)
  2757. end
  2758. end
  2759. def test_cross_thread_close_stdio
  2760. assert_separately([], <<-'end;')
  2761. IO.pipe do |r,w|
  2762. $stdin.reopen(r)
  2763. r.close
  2764. read_thread = Thread.new do
  2765. begin
  2766. $stdin.read(1)
  2767. rescue IOError => e
  2768. e
  2769. end
  2770. end
  2771. sleep(0.1) until read_thread.stop?
  2772. $stdin.close
  2773. assert_kind_of(IOError, read_thread.value)
  2774. end
  2775. end;
  2776. end
  2777. def test_single_exception_on_close
  2778. a = []
  2779. t = []
  2780. 10.times do
  2781. r, w = IO.pipe
  2782. a << [r, w]
  2783. t << Thread.new do
  2784. while r.gets
  2785. end rescue IOError
  2786. Thread.current.pending_interrupt?
  2787. end
  2788. end
  2789. a.each do |r, w|
  2790. w.write(-"\n")
  2791. w.close
  2792. r.close
  2793. end
  2794. t.each do |th|
  2795. assert_equal false, th.value, '[ruby-core:81581] [Bug #13632]'
  2796. end
  2797. end
  2798. def test_open_mode
  2799. feature4742 = "[ruby-core:36338]"
  2800. bug6055 = '[ruby-dev:45268]'
  2801. mkcdtmpdir do
  2802. assert_not_nil(f = File.open('symbolic', 'w'))
  2803. f.close
  2804. assert_not_nil(f = File.open('numeric', File::WRONLY|File::TRUNC|File::CREAT))
  2805. f.close
  2806. assert_not_nil(f = File.open('hash-symbolic', :mode => 'w'))
  2807. f.close
  2808. assert_not_nil(f = File.open('hash-numeric', :mode => File::WRONLY|File::TRUNC|File::CREAT), feature4742)
  2809. f.close
  2810. assert_nothing_raised(bug6055) {f = File.open('hash-symbolic', binmode: true)}
  2811. f.close
  2812. end
  2813. end
  2814. def test_s_write
  2815. mkcdtmpdir do
  2816. path = "test_s_write"
  2817. File.write(path, "foo\nbar\nbaz")
  2818. assert_equal("foo\nbar\nbaz", File.read(path))
  2819. File.write(path, "FOO", 0)
  2820. assert_equal("FOO\nbar\nbaz", File.read(path))
  2821. File.write(path, "BAR")
  2822. assert_equal("BAR", File.read(path))
  2823. File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP")
  2824. assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP"))
  2825. File.delete path
  2826. assert_equal(6, File.write(path, 'string', 2))
  2827. File.delete path
  2828. assert_raise(Errno::EINVAL) { File.write('nonexisting','string', -2) }
  2829. assert_equal(6, File.write(path, 'string'))
  2830. assert_equal(3, File.write(path, 'sub', 1))
  2831. assert_equal("ssubng", File.read(path))
  2832. File.delete path
  2833. assert_equal(3, File.write(path, "foo", encoding: "UTF-8"))
  2834. File.delete path
  2835. assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8"))
  2836. assert_equal("foo", File.read(path))
  2837. assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
  2838. assert_equal("ffo", File.read(path))
  2839. File.delete path
  2840. assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
  2841. assert_equal("\00f", File.read(path))
  2842. assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8"))
  2843. assert_equal("ff", File.read(path))
  2844. File.write(path, "foo", Object.new => Object.new)
  2845. assert_equal("foo", File.read(path))
  2846. end
  2847. end
  2848. def test_s_binread_does_not_leak_with_invalid_offset
  2849. assert_raise(Errno::EINVAL) { IO.binread(__FILE__, 0, -1) }
  2850. end
  2851. def test_s_binwrite
  2852. mkcdtmpdir do
  2853. path = "test_s_binwrite"
  2854. File.binwrite(path, "foo\nbar\nbaz")
  2855. assert_equal("foo\nbar\nbaz", File.read(path))
  2856. File.binwrite(path, "FOO", 0)
  2857. assert_equal("FOO\nbar\nbaz", File.read(path))
  2858. File.binwrite(path, "BAR")
  2859. assert_equal("BAR", File.read(path))
  2860. File.binwrite(path, "\u{3042}")
  2861. assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path))
  2862. File.delete path
  2863. assert_equal(6, File.binwrite(path, 'string', 2))
  2864. File.delete path
  2865. assert_equal(6, File.binwrite(path, 'string'))
  2866. assert_equal(3, File.binwrite(path, 'sub', 1))
  2867. assert_equal("ssubng", File.binread(path))
  2868. assert_equal(6, File.size(path))
  2869. assert_raise(Errno::EINVAL) { File.binwrite('nonexisting', 'string', -2) }
  2870. assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") }
  2871. end
  2872. end
  2873. def test_race_between_read
  2874. Tempfile.create("test") {|file|
  2875. begin
  2876. path = file.path
  2877. file.close
  2878. write_file = File.open(path, "wt")
  2879. read_file = File.open(path, "rt")
  2880. threads = []
  2881. 10.times do |i|
  2882. threads << Thread.new {write_file.print(i)}
  2883. threads << Thread.new {read_file.read}
  2884. end
  2885. assert_join_threads(threads)
  2886. assert(true, "[ruby-core:37197]")
  2887. ensure
  2888. read_file.close
  2889. write_file.close
  2890. end
  2891. }
  2892. end
  2893. def test_warn
  2894. assert_warning "warning\n" do
  2895. warn "warning"
  2896. end
  2897. assert_warning '' do
  2898. warn
  2899. end
  2900. assert_warning "[Feature #5029]\n[ruby-core:38070]\n" do
  2901. warn "[Feature #5029]", "[ruby-core:38070]"
  2902. end
  2903. end
  2904. def test_cloexec
  2905. return unless defined? Fcntl::FD_CLOEXEC
  2906. open(__FILE__) {|f|
  2907. assert_predicate(f, :close_on_exec?)
  2908. g = f.dup
  2909. begin
  2910. assert_predicate(g, :close_on_exec?)
  2911. f.reopen(g)
  2912. assert_predicate(f, :close_on_exec?)
  2913. ensure
  2914. g.close
  2915. end
  2916. g = IO.new(f.fcntl(Fcntl::F_DUPFD))
  2917. begin
  2918. assert_predicate(g, :close_on_exec?)
  2919. ensure
  2920. g.close
  2921. end
  2922. }
  2923. IO.pipe {|r,w|
  2924. assert_predicate(r, :close_on_exec?)
  2925. assert_predicate(w, :close_on_exec?)
  2926. }
  2927. end
  2928. def test_ioctl_linux
  2929. # Alpha, mips, sparc and ppc have an another ioctl request number scheme.
  2930. # So, hardcoded 0x80045200 may fail.
  2931. assert_nothing_raised do
  2932. File.open('/dev/urandom'){|f1|
  2933. entropy_count = ""
  2934. # RNDGETENTCNT(0x80045200) mean "get entropy count".
  2935. f1.ioctl(0x80045200, entropy_count)
  2936. }
  2937. end
  2938. buf = ''
  2939. assert_nothing_raised do
  2940. fionread = 0x541B
  2941. File.open(__FILE__){|f1|
  2942. f1.ioctl(fionread, buf)
  2943. }
  2944. end
  2945. assert_equal(File.size(__FILE__), buf.unpack('i!')[0])
  2946. end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
  2947. def test_ioctl_linux2
  2948. return unless STDIN.tty? # stdin is not a terminal
  2949. begin
  2950. f = File.open('/dev/tty')
  2951. rescue Errno::ENOENT, Errno::ENXIO => e
  2952. skip e.message
  2953. else
  2954. tiocgwinsz=0x5413
  2955. winsize=""
  2956. assert_nothing_raised {
  2957. f.ioctl(tiocgwinsz, winsize)
  2958. }
  2959. ensure
  2960. f&.close
  2961. end
  2962. end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
  2963. def test_setpos
  2964. mkcdtmpdir {
  2965. File.open("tmp.txt", "wb") {|f|
  2966. f.puts "a"
  2967. f.puts "bc"
  2968. f.puts "def"
  2969. }
  2970. pos1 = pos2 = pos3 = nil
  2971. File.open("tmp.txt", "rb") {|f|
  2972. assert_equal("a\n", f.gets)
  2973. pos1 = f.pos
  2974. assert_equal("bc\n", f.gets)
  2975. pos2 = f.pos
  2976. assert_equal("def\n", f.gets)
  2977. pos3 = f.pos
  2978. assert_equal(nil, f.gets)
  2979. }
  2980. File.open("tmp.txt", "rb") {|f|
  2981. f.pos = pos1
  2982. assert_equal("bc\n", f.gets)
  2983. assert_equal("def\n", f.gets)
  2984. assert_equal(nil, f.gets)
  2985. }
  2986. File.open("tmp.txt", "rb") {|f|
  2987. f.pos = pos2
  2988. assert_equal("def\n", f.gets)
  2989. assert_equal(nil, f.gets)
  2990. }
  2991. File.open("tmp.txt", "rb") {|f|
  2992. f.pos = pos3
  2993. assert_equal(nil, f.gets)
  2994. }
  2995. File.open("tmp.txt", "rb") {|f|
  2996. f.pos = File.size("tmp.txt")
  2997. s = "not empty string "
  2998. assert_equal("", f.read(0,s))
  2999. }
  3000. }
  3001. end
  3002. def test_std_fileno
  3003. assert_equal(0, STDIN.fileno)
  3004. assert_equal(1, STDOUT.fileno)
  3005. assert_equal(2, STDERR.fileno)
  3006. assert_equal(0, $stdin.fileno)
  3007. assert_equal(1, $stdout.fileno)
  3008. assert_equal(2, $stderr.fileno)
  3009. end
  3010. def test_frozen_fileno
  3011. bug9865 = '[ruby-dev:48241] [Bug #9865]'
  3012. with_pipe do |r,w|
  3013. fd = r.fileno
  3014. assert_equal(fd, r.freeze.fileno, bug9865)
  3015. end
  3016. end
  3017. def test_frozen_autoclose
  3018. with_pipe do |r,w|
  3019. assert_equal(true, r.freeze.autoclose?)
  3020. end
  3021. end
  3022. def test_sysread_locktmp
  3023. bug6099 = '[ruby-dev:45297]'
  3024. buf = " " * 100
  3025. data = "a" * 100
  3026. with_pipe do |r,w|
  3027. th = Thread.new {r.sysread(100, buf)}
  3028. Thread.pass until th.stop?
  3029. buf.replace("")
  3030. assert_empty(buf, bug6099)
  3031. w.write(data)
  3032. Thread.pass while th.alive?
  3033. th.join
  3034. end
  3035. assert_equal(data, buf, bug6099)
  3036. end
  3037. def test_readpartial_locktmp
  3038. bug6099 = '[ruby-dev:45297]'
  3039. buf = " " * 100
  3040. data = "a" * 100
  3041. th = nil
  3042. with_pipe do |r,w|
  3043. r.nonblock = true
  3044. th = Thread.new {r.readpartial(100, buf)}
  3045. Thread.pass until th.stop?
  3046. assert_equal 100, buf.bytesize
  3047. msg = /can't modify string; temporarily locked/
  3048. assert_raise_with_message(RuntimeError, msg) do
  3049. buf.replace("")
  3050. end
  3051. assert_predicate(th, :alive?)
  3052. w.write(data)
  3053. th.join
  3054. end
  3055. assert_equal(data, buf, bug6099)
  3056. end
  3057. def test_advise_pipe
  3058. # we don't know if other platforms have a real posix_fadvise()
  3059. with_pipe do |r,w|
  3060. # Linux 2.6.15 and earlier returned EINVAL instead of ESPIPE
  3061. assert_raise(Errno::ESPIPE, Errno::EINVAL) {
  3062. r.advise(:willneed) or skip "fadvise(2) is not implemented"
  3063. }
  3064. assert_raise(Errno::ESPIPE, Errno::EINVAL) {
  3065. w.advise(:willneed) or skip "fadvise(2) is not implemented"
  3066. }
  3067. end
  3068. end if /linux/ =~ RUBY_PLATFORM
  3069. def assert_buffer_not_raise_shared_string_error
  3070. bug6764 = '[ruby-core:46586]'
  3071. bug9847 = '[ruby-core:62643] [Bug #9847]'
  3072. size = 28
  3073. data = [*"a".."z", *"A".."Z"].shuffle.join("")
  3074. t = Tempfile.new("test_io")
  3075. t.write(data)
  3076. t.close
  3077. w = []
  3078. assert_nothing_raised(RuntimeError, bug6764) do
  3079. buf = ''
  3080. File.open(t.path, "r") do |r|
  3081. while yield(r, size, buf)
  3082. w << buf.dup
  3083. end
  3084. end
  3085. end
  3086. assert_equal(data, w.join(""), bug9847)
  3087. ensure
  3088. t.close!
  3089. end
  3090. def test_read_buffer_not_raise_shared_string_error
  3091. assert_buffer_not_raise_shared_string_error do |r, size, buf|
  3092. r.read(size, buf)
  3093. end
  3094. end
  3095. def test_sysread_buffer_not_raise_shared_string_error
  3096. assert_buffer_not_raise_shared_string_error do |r, size, buf|
  3097. begin
  3098. r.sysread(size, buf)
  3099. rescue EOFError
  3100. nil
  3101. end
  3102. end
  3103. end
  3104. def test_readpartial_buffer_not_raise_shared_string_error
  3105. assert_buffer_not_raise_shared_string_error do |r, size, buf|
  3106. begin
  3107. r.readpartial(size, buf)
  3108. rescue EOFError
  3109. nil
  3110. end
  3111. end
  3112. end
  3113. def test_puts_recursive_ary
  3114. bug5986 = '[ruby-core:42444]'
  3115. c = Class.new {
  3116. def to_ary
  3117. [self]
  3118. end
  3119. }
  3120. s = StringIO.new
  3121. s.puts(c.new)
  3122. assert_equal("[...]\n", s.string, bug5986)
  3123. end
  3124. def test_io_select_with_many_files
  3125. bug8080 = '[ruby-core:53349]'
  3126. assert_normal_exit %q{
  3127. require "tempfile"
  3128. # Unfortunately, ruby doesn't export FD_SETSIZE. then we assume it's 1024.
  3129. fd_setsize = 1024
  3130. # try to raise RLIM_NOFILE to >FD_SETSIZE
  3131. begin
  3132. Process.setrlimit(Process::RLIMIT_NOFILE, fd_setsize+20)
  3133. rescue Errno::EPERM
  3134. exit 0
  3135. end
  3136. tempfiles = []
  3137. (0..fd_setsize+1).map {|i|
  3138. tempfiles << Tempfile.open("test_io_select_with_many_files")
  3139. }
  3140. IO.select(tempfiles)
  3141. }, bug8080, timeout: 100
  3142. end if defined?(Process::RLIMIT_NOFILE)
  3143. def test_read_32bit_boundary
  3144. bug8431 = '[ruby-core:55098] [Bug #8431]'
  3145. make_tempfile {|t|
  3146. assert_separately(["-", bug8431, t.path], <<-"end;")
  3147. msg = ARGV.shift
  3148. f = open(ARGV[0], "rb")
  3149. f.seek(0xffff_ffff)
  3150. assert_nil(f.read(1), msg)
  3151. end;
  3152. }
  3153. end if /mswin|mingw/ =~ RUBY_PLATFORM
  3154. def test_write_32bit_boundary
  3155. bug8431 = '[ruby-core:55098] [Bug #8431]'
  3156. make_tempfile {|t|
  3157. def t.close(unlink_now = false)
  3158. # TODO: Tempfile should deal with this delay on Windows?
  3159. # NOTE: re-opening with O_TEMPORARY does not work.
  3160. path = self.path
  3161. ret = super
  3162. if unlink_now
  3163. begin
  3164. File.unlink(path)
  3165. rescue Errno::ENOENT
  3166. rescue Errno::EACCES
  3167. sleep(2)
  3168. retry
  3169. end
  3170. end
  3171. ret
  3172. end
  3173. begin
  3174. assert_separately(["-", bug8431, t.path], <<-"end;", timeout: 30)
  3175. msg = ARGV.shift
  3176. f = open(ARGV[0], "wb")
  3177. f.seek(0xffff_ffff)
  3178. begin
  3179. # this will consume very long time or fail by ENOSPC on a
  3180. # filesystem which sparse file is not supported
  3181. f.write('1')
  3182. pos = f.tell
  3183. rescue Errno::ENOSPC
  3184. skip "non-sparse file system"
  3185. rescue SystemCallError
  3186. else
  3187. assert_equal(0x1_0000_0000, pos, msg)
  3188. end
  3189. end;
  3190. rescue Timeout::Error
  3191. skip "Timeout because of slow file writing"
  3192. end
  3193. }
  3194. end if /mswin|mingw/ =~ RUBY_PLATFORM
  3195. def test_read_unlocktmp_ensure
  3196. bug8669 = '[ruby-core:56121] [Bug #8669]'
  3197. str = ""
  3198. IO.pipe {|r,|
  3199. t = Thread.new {
  3200. assert_raise(RuntimeError) {
  3201. r.read(nil, str)
  3202. }
  3203. }
  3204. sleep 0.1 until t.stop?
  3205. t.raise
  3206. sleep 0.1 while t.alive?
  3207. assert_nothing_raised(RuntimeError, bug8669) { str.clear }
  3208. t.join
  3209. }
  3210. end if /cygwin/ !~ RUBY_PLATFORM
  3211. def test_readpartial_unlocktmp_ensure
  3212. bug8669 = '[ruby-core:56121] [Bug #8669]'
  3213. str = ""
  3214. IO.pipe {|r, w|
  3215. t = Thread.new {
  3216. assert_raise(RuntimeError) {
  3217. r.readpartial(4096, str)
  3218. }
  3219. }
  3220. sleep 0.1 until t.stop?
  3221. t.raise
  3222. sleep 0.1 while t.alive?
  3223. assert_nothing_raised(RuntimeError, bug8669) { str.clear }
  3224. t.join
  3225. }
  3226. end if /cygwin/ !~ RUBY_PLATFORM
  3227. def test_readpartial_bad_args
  3228. IO.pipe do |r, w|
  3229. w.write '.'
  3230. buf = String.new
  3231. assert_raise(ArgumentError) { r.readpartial(1, buf, exception: false) }
  3232. assert_raise(TypeError) { r.readpartial(1, exception: false) }
  3233. assert_equal [[r],[],[]], IO.select([r], nil, nil, 1)
  3234. assert_equal '.', r.readpartial(1)
  3235. end
  3236. end
  3237. def test_sysread_unlocktmp_ensure
  3238. bug8669 = '[ruby-core:56121] [Bug #8669]'
  3239. str = ""
  3240. IO.pipe {|r, w|
  3241. t = Thread.new {
  3242. assert_raise(RuntimeError) {
  3243. r.sysread(4096, str)
  3244. }
  3245. }
  3246. sleep 0.1 until t.stop?
  3247. t.raise
  3248. sleep 0.1 while t.alive?
  3249. assert_nothing_raised(RuntimeError, bug8669) { str.clear }
  3250. t.join
  3251. }
  3252. end if /cygwin/ !~ RUBY_PLATFORM
  3253. def test_exception_at_close
  3254. bug10153 = '[ruby-core:64463] [Bug #10153] exception in close at the end of block'
  3255. assert_raise(Errno::EBADF, bug10153) do
  3256. IO.pipe do |r, w|
  3257. assert_nothing_raised {IO.open(w.fileno) {}}
  3258. end
  3259. end
  3260. end
  3261. def test_close_twice
  3262. open(__FILE__) {|f|
  3263. assert_equal(nil, f.close)
  3264. assert_equal(nil, f.close)
  3265. }
  3266. end
  3267. def test_close_uninitialized
  3268. io = IO.allocate
  3269. assert_raise(IOError) { io.close }
  3270. end
  3271. def test_open_fifo_does_not_block_other_threads
  3272. mkcdtmpdir {
  3273. File.mkfifo("fifo")
  3274. assert_separately([], <<-'EOS')
  3275. t1 = Thread.new {
  3276. open("fifo", "r") {|r|
  3277. r.read
  3278. }
  3279. }
  3280. t2 = Thread.new {
  3281. open("fifo", "w") {|w|
  3282. w.write "foo"
  3283. }
  3284. }
  3285. t1_value, _ = assert_join_threads([t1, t2])
  3286. assert_equal("foo", t1_value)
  3287. EOS
  3288. }
  3289. end if /mswin|mingw|bccwin|cygwin/ !~ RUBY_PLATFORM
  3290. def test_open_flag
  3291. make_tempfile do |t|
  3292. assert_raise(Errno::EEXIST){ open(t.path, File::WRONLY|File::CREAT, flags: File::EXCL){} }
  3293. assert_raise(Errno::EEXIST){ open(t.path, 'w', flags: File::EXCL){} }
  3294. assert_raise(Errno::EEXIST){ open(t.path, mode: 'w', flags: File::EXCL){} }
  3295. end
  3296. end
  3297. def test_open_flag_binary
  3298. binary_enc = Encoding.find("BINARY")
  3299. make_tempfile do |t|
  3300. open(t.path, File::RDONLY, flags: File::BINARY) do |f|
  3301. assert_equal true, f.binmode?
  3302. assert_equal binary_enc, f.external_encoding
  3303. end
  3304. open(t.path, 'r', flags: File::BINARY) do |f|
  3305. assert_equal true, f.binmode?
  3306. assert_equal binary_enc, f.external_encoding
  3307. end
  3308. open(t.path, mode: 'r', flags: File::BINARY) do |f|
  3309. assert_equal true, f.binmode?
  3310. assert_equal binary_enc, f.external_encoding
  3311. end
  3312. open(t.path, File::RDONLY|File::BINARY) do |f|
  3313. assert_equal true, f.binmode?
  3314. assert_equal binary_enc, f.external_encoding
  3315. end
  3316. open(t.path, File::RDONLY|File::BINARY, autoclose: true) do |f|
  3317. assert_equal true, f.binmode?
  3318. assert_equal binary_enc, f.external_encoding
  3319. end
  3320. end
  3321. end if File::BINARY != 0
  3322. def test_exclusive_mode
  3323. make_tempfile do |t|
  3324. assert_raise(Errno::EEXIST){ open(t.path, 'wx'){} }
  3325. assert_raise(ArgumentError){ open(t.path, 'rx'){} }
  3326. assert_raise(ArgumentError){ open(t.path, 'ax'){} }
  3327. end
  3328. end
  3329. def test_race_gets_and_close
  3330. opt = { signal: :ABRT, timeout: 200 }
  3331. assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", **opt)
  3332. bug13076 = '[ruby-core:78845] [Bug #13076]'
  3333. begin;
  3334. 10.times do |i|
  3335. a = []
  3336. t = []
  3337. 10.times do
  3338. r,w = IO.pipe
  3339. a << [r,w]
  3340. t << Thread.new do
  3341. begin
  3342. while r.gets
  3343. end
  3344. rescue IOError
  3345. end
  3346. end
  3347. end
  3348. a.each do |r,w|
  3349. w.puts "hoge"
  3350. w.close
  3351. r.close
  3352. end
  3353. t.each do |th|
  3354. assert_same(th, th.join(2), bug13076)
  3355. end
  3356. end
  3357. end;
  3358. end
  3359. def test_race_closed_stream
  3360. assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
  3361. begin;
  3362. bug13158 = '[ruby-core:79262] [Bug #13158]'
  3363. closed = nil
  3364. q = Queue.new
  3365. IO.pipe do |r, w|
  3366. thread = Thread.new do
  3367. begin
  3368. q << true
  3369. assert_raise_with_message(IOError, /stream closed/) do
  3370. while r.gets
  3371. end
  3372. end
  3373. ensure
  3374. closed = r.closed?
  3375. end
  3376. end
  3377. q.pop
  3378. sleep 0.01 until thread.stop?
  3379. r.close
  3380. thread.join
  3381. assert_equal(true, closed, bug13158 + ': stream should be closed')
  3382. end
  3383. end;
  3384. end
  3385. if RUBY_ENGINE == "ruby" # implementation details
  3386. def test_foreach_rs_conversion
  3387. make_tempfile {|t|
  3388. a = []
  3389. rs = Struct.new(:count).new(0)
  3390. def rs.to_str; self.count += 1; "\n"; end
  3391. IO.foreach(t.path, rs) {|x| a << x }
  3392. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  3393. assert_equal(1, rs.count)
  3394. }
  3395. end
  3396. def test_foreach_rs_invalid
  3397. make_tempfile {|t|
  3398. rs = Object.new
  3399. def rs.to_str; raise "invalid rs"; end
  3400. assert_raise(RuntimeError) do
  3401. IO.foreach(t.path, rs, mode:"w") {}
  3402. end
  3403. assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
  3404. }
  3405. end
  3406. def test_foreach_limit_conversion
  3407. make_tempfile {|t|
  3408. a = []
  3409. lim = Struct.new(:count).new(0)
  3410. def lim.to_int; self.count += 1; -1; end
  3411. IO.foreach(t.path, lim) {|x| a << x }
  3412. assert_equal(["foo\n", "bar\n", "baz\n"], a)
  3413. assert_equal(1, lim.count)
  3414. }
  3415. end
  3416. def test_foreach_limit_invalid
  3417. make_tempfile {|t|
  3418. lim = Object.new
  3419. def lim.to_int; raise "invalid limit"; end
  3420. assert_raise(RuntimeError) do
  3421. IO.foreach(t.path, lim, mode:"w") {}
  3422. end
  3423. assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
  3424. }
  3425. end
  3426. def test_readlines_rs_invalid
  3427. make_tempfile {|t|
  3428. rs = Object.new
  3429. def rs.to_str; raise "invalid rs"; end
  3430. assert_raise(RuntimeError) do
  3431. IO.readlines(t.path, rs, mode:"w")
  3432. end
  3433. assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
  3434. }
  3435. end
  3436. def test_readlines_limit_invalid
  3437. make_tempfile {|t|
  3438. lim = Object.new
  3439. def lim.to_int; raise "invalid limit"; end
  3440. assert_raise(RuntimeError) do
  3441. IO.readlines(t.path, lim, mode:"w")
  3442. end
  3443. assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
  3444. }
  3445. end
  3446. def test_closed_stream_in_rescue
  3447. assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
  3448. begin;
  3449. 10.times do
  3450. assert_nothing_raised(RuntimeError, /frozen IOError/) do
  3451. IO.pipe do |r, w|
  3452. th = Thread.start {r.close}
  3453. r.gets
  3454. rescue IOError
  3455. # swallow pending exceptions
  3456. begin
  3457. sleep 0.001
  3458. rescue IOError
  3459. retry
  3460. end
  3461. ensure
  3462. th.kill.join
  3463. end
  3464. end
  3465. end
  3466. end;
  3467. end
  3468. def test_write_no_garbage
  3469. skip "multiple threads already active" if Thread.list.size > 1
  3470. res = {}
  3471. ObjectSpace.count_objects(res) # creates strings on first call
  3472. [ 'foo'.b, '*' * 24 ].each do |buf|
  3473. with_pipe do |r, w|
  3474. GC.disable
  3475. begin
  3476. before = ObjectSpace.count_objects(res)[:T_STRING]
  3477. n = w.write(buf)
  3478. s = w.syswrite(buf)
  3479. after = ObjectSpace.count_objects(res)[:T_STRING]
  3480. ensure
  3481. GC.enable
  3482. end
  3483. assert_equal before, after,
  3484. "no strings left over after write [ruby-core:78898] [Bug #13085]: #{ before } strings before write -> #{ after } strings after write"
  3485. assert_not_predicate buf, :frozen?, 'no inadvertent freeze'
  3486. assert_equal buf.bytesize, n, 'IO#write wrote expected size'
  3487. assert_equal s, n, 'IO#syswrite wrote expected size'
  3488. end
  3489. end
  3490. end
  3491. end
  3492. def test_pread
  3493. make_tempfile { |t|
  3494. open(t.path) do |f|
  3495. assert_equal("bar", f.pread(3, 4))
  3496. buf = "asdf"
  3497. assert_equal("bar", f.pread(3, 4, buf))
  3498. assert_equal("bar", buf)
  3499. assert_raise(EOFError) { f.pread(1, f.size) }
  3500. end
  3501. }
  3502. end if IO.method_defined?(:pread)
  3503. def test_pwrite
  3504. make_tempfile { |t|
  3505. open(t.path, IO::RDWR) do |f|
  3506. assert_equal(3, f.pwrite("ooo", 4))
  3507. assert_equal("ooo", f.pread(3, 4))
  3508. end
  3509. }
  3510. end if IO.method_defined?(:pread) and IO.method_defined?(:pwrite)
  3511. def test_select_exceptfds
  3512. if Etc.uname[:sysname] == 'SunOS'
  3513. str = 'h'.freeze #(???) Only 1 byte with MSG_OOB on Solaris
  3514. else
  3515. str = 'hello'.freeze
  3516. end
  3517. TCPServer.open('localhost', 0) do |svr|
  3518. con = TCPSocket.new('localhost', svr.addr[1])
  3519. acc = svr.accept
  3520. assert_equal str.length, con.send(str, Socket::MSG_OOB)
  3521. set = IO.select(nil, nil, [acc], 30)
  3522. assert_equal([[], [], [acc]], set, 'IO#select exceptions array OK')
  3523. acc.close
  3524. con.close
  3525. end
  3526. end if Socket.const_defined?(:MSG_OOB)
  3527. def test_recycled_fd_close
  3528. dot = -'.'
  3529. IO.pipe do |sig_rd, sig_wr|
  3530. noex = Thread.new do # everything right and never see exceptions :)
  3531. until sig_rd.wait_readable(0)
  3532. IO.pipe do |r, w|
  3533. th = Thread.new { r.read(1) }
  3534. w.write(dot)
  3535. assert_same th, th.join(15), '"good" reader timeout'
  3536. assert_equal(dot, th.value)
  3537. end
  3538. end
  3539. sig_rd.read(4)
  3540. end
  3541. 1000.times do |i| # stupid things and make exceptions:
  3542. IO.pipe do |r,w|
  3543. th = Thread.new do
  3544. begin
  3545. while r.gets
  3546. end
  3547. rescue IOError => e
  3548. e
  3549. end
  3550. end
  3551. Thread.pass until th.stop?
  3552. r.close
  3553. assert_same th, th.join(30), '"bad" reader timeout'
  3554. assert_match(/stream closed/, th.value.message)
  3555. end
  3556. end
  3557. sig_wr.write 'done'
  3558. assert_same noex, noex.join(20), '"good" writer timeout'
  3559. assert_equal 'done', noex.value ,'r63216'
  3560. end
  3561. end
  3562. def test_select_memory_leak
  3563. # avoid malloc arena explosion from glibc and jemalloc:
  3564. env = {
  3565. 'MALLOC_ARENA_MAX' => '1',
  3566. 'MALLOC_ARENA_TEST' => '1',
  3567. 'MALLOC_CONF' => 'narenas:1',
  3568. }
  3569. assert_no_memory_leak([env], "#{<<~"begin;"}\n#{<<~'else;'}", "#{<<~'end;'}", rss: true, timeout: 60)
  3570. begin;
  3571. r, w = IO.pipe
  3572. rset = [r]
  3573. wset = [w]
  3574. exc = StandardError.new(-"select used to leak on exception")
  3575. exc.set_backtrace([])
  3576. Thread.new { IO.select(rset, wset, nil, 0) }.join
  3577. else;
  3578. th = Thread.new do
  3579. Thread.handle_interrupt(StandardError => :on_blocking) do
  3580. begin
  3581. IO.select(rset, wset)
  3582. rescue
  3583. retry
  3584. end while true
  3585. end
  3586. end
  3587. 50_000.times do
  3588. Thread.pass until th.stop?
  3589. th.raise(exc)
  3590. end
  3591. th.kill
  3592. th.join
  3593. end;
  3594. end
  3595. def test_external_encoding_index
  3596. IO.pipe {|r, w|
  3597. assert_raise(TypeError) {Marshal.dump(r)}
  3598. assert_raise(TypeError) {Marshal.dump(w)}
  3599. }
  3600. end
  3601. def test_stdout_to_closed_pipe
  3602. EnvUtil.invoke_ruby(["-e", "loop {puts :ok}"], "", true, true) do
  3603. |in_p, out_p, err_p, pid|
  3604. out = out_p.gets
  3605. out_p.close
  3606. err = err_p.read
  3607. ensure
  3608. status = Process.wait2(pid)[1]
  3609. assert_equal("ok\n", out)
  3610. assert_empty(err)
  3611. assert_not_predicate(status, :success?)
  3612. if Signal.list["PIPE"]
  3613. assert_predicate(status, :signaled?)
  3614. assert_equal("PIPE", Signal.signame(status.termsig) || status.termsig)
  3615. end
  3616. end
  3617. end
  3618. end