open4 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/lib/open4.rb +19 -2
  2. data/open4.gemspec +2 -1
  3. data/test/popen4ext_test.rb +82 -0
  4. metadata +21 -42
@@ -4,7 +4,7 @@ require 'timeout'
4
4
  require 'thread'
5
5
 
6
6
  module Open4
7
- VERSION = '1.2.0'
7
+ VERSION = '1.3.0'
8
8
  def self.version() VERSION end
9
9
 
10
10
  class Error < ::StandardError; end
@@ -38,7 +38,17 @@ module Open4
38
38
  module_function :popen4
39
39
  module_function :open4
40
40
 
41
- def self.do_popen(b = nil, exception_propagation_at = nil, &cmd)
41
+ def popen4ext(closefds=false, *cmd, &b)
42
+ Open4.do_popen(b, :init, closefds) do |ps_read, ps_write|
43
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
44
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
+ exec(*cmd)
46
+ raise 'forty-two' # Is this really needed?
47
+ end
48
+ end
49
+ module_function :popen4ext
50
+
51
+ def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
42
52
  pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
43
53
 
44
54
  verbose = $VERBOSE
@@ -46,6 +56,13 @@ module Open4
46
56
  $VERBOSE = nil
47
57
 
48
58
  cid = fork {
59
+ if closefds
60
+ exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
61
+ ObjectSpace.each_object(IO){|io|
62
+ io.close if (not io.closed?) and (not exlist.include? io.fileno)
63
+ }
64
+ end
65
+
49
66
  pw.last.close
50
67
  STDIN.reopen pw.first
51
68
  pw.first.close
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "open4"
6
- spec.version = "1.2.0"
6
+ spec.version = "1.3.0"
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.summary = "open4"
9
9
  spec.description = "description: open4 kicks the ass"
@@ -29,6 +29,7 @@ Gem::Specification::new do |spec|
29
29
  "test",
30
30
  "test/pfork4_test.rb",
31
31
  "test/popen4_test.rb",
32
+ "test/popen4ext_test.rb",
32
33
  "test/support",
33
34
  "test/support/test_case.rb",
34
35
  "white_box",
@@ -0,0 +1,82 @@
1
+ require 'test_case'
2
+
3
+ module Open4
4
+
5
+ class POpen4Test < TestCase
6
+ UNKNOWN_CMD = 'asdfadsfjlkkk'
7
+ UNKNOWN_CMD_ERRORS = [Errno::ENOENT, Errno::EINVAL]
8
+
9
+ def test_unknown_command_propagates_exception
10
+ err = assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
11
+ assert_match /#{UNKNOWN_CMD}/, err.to_s if on_mri?
12
+ end
13
+
14
+ def test_exception_propagation_avoids_zombie_child_process
15
+ assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
16
+ assert_empty Process.waitall
17
+ end
18
+
19
+ def test_exit_failure
20
+ code = 43
21
+ cid, _ = popen4ext true, %{ruby -e "exit #{43}"}
22
+ assert_equal code, wait_status(cid)
23
+ end
24
+
25
+ def test_exit_success
26
+ cid, _ = popen4ext true, %{ruby -e "exit"}
27
+ assert_equal 0, wait_status(cid)
28
+ end
29
+
30
+ def test_passes_child_pid_to_block
31
+ cmd = %{ruby -e "STDOUT.print Process.pid"}
32
+ cid_in_block = nil
33
+ cid_in_fun = nil
34
+ status = popen4ext(true, cmd) do |cid, _, stdout, _|
35
+ cid_in_block = cid
36
+ cid_in_fun = stdout.read.to_i
37
+ end
38
+ assert_equal cid_in_fun, cid_in_block
39
+ end
40
+
41
+ def test_io_pipes_without_block
42
+ via_msg = 'foo'
43
+ err_msg = 'bar'
44
+ cmd = <<-END
45
+ ruby -e "
46
+ STDOUT.write STDIN.read
47
+ STDERR.write '#{err_msg}'
48
+ "
49
+ END
50
+ cid, stdin, stdout, stderr = popen4ext true, cmd
51
+ stdin.write via_msg
52
+ stdin.close
53
+ out_actual = stdout.read
54
+ err_actual = stderr.read
55
+ assert_equal via_msg, out_actual
56
+ assert_equal err_msg, err_actual
57
+ assert_equal 0, wait_status(cid)
58
+ end
59
+
60
+ def test_io_pipes_with_block
61
+ via_msg = 'foo'
62
+ err_msg = 'bar'
63
+ out_actual, err_actual = nil
64
+ cmd = <<-END
65
+ ruby -e "
66
+ STDOUT.write STDIN.read
67
+ STDERR.write '#{err_msg}'
68
+ "
69
+ END
70
+ status = popen4ext(true, cmd) do |_, stdin, stdout, stderr|
71
+ stdin.write via_msg
72
+ stdin.close
73
+ out_actual = stdout.read
74
+ err_actual = stderr.read
75
+ end
76
+ assert_equal via_msg, out_actual
77
+ assert_equal err_msg, err_actual
78
+ assert_equal 0, status.exitstatus
79
+ end
80
+ end
81
+
82
+ end
metadata CHANGED
@@ -1,32 +1,22 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: open4
3
- version: !ruby/object:Gem::Version
4
- hash: 31
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 2
9
- - 0
10
- version: 1.2.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Ara T. Howard
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-10-05 00:00:00 Z
12
+ date: 2011-11-19 00:00:00.000000000 Z
19
13
  dependencies: []
20
-
21
- description: "description: open4 kicks the ass"
14
+ description: ! 'description: open4 kicks the ass'
22
15
  email: ara.t.howard@gmail.com
23
16
  executables: []
24
-
25
17
  extensions: []
26
-
27
18
  extra_rdoc_files: []
28
-
29
- files:
19
+ files:
30
20
  - LICENSE
31
21
  - README
32
22
  - README.erb
@@ -44,42 +34,31 @@ files:
44
34
  - samples/timeout.rb
45
35
  - test/pfork4_test.rb
46
36
  - test/popen4_test.rb
37
+ - test/popen4ext_test.rb
47
38
  - test/support/test_case.rb
48
39
  - white_box/leak.rb
49
40
  homepage: https://github.com/ahoward/open4
50
41
  licenses: []
51
-
52
- metadata: {}
53
-
54
42
  post_install_message:
55
43
  rdoc_options: []
56
-
57
- require_paths:
44
+ require_paths:
58
45
  - lib
59
- required_ruby_version: !ruby/object:Gem::Requirement
46
+ required_ruby_version: !ruby/object:Gem::Requirement
60
47
  none: false
61
- requirements:
62
- - - ">="
63
- - !ruby/object:Gem::Version
64
- hash: 3
65
- segments:
66
- - 0
67
- version: "0"
68
- required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
53
  none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
77
58
  requirements: []
78
-
79
59
  rubyforge_project: codeforpeople
80
- rubygems_version: 1.8.10
60
+ rubygems_version: 1.8.11
81
61
  signing_key:
82
- specification_version: 4
62
+ specification_version: 3
83
63
  summary: open4
84
64
  test_files: []
85
-