open4 1.0.1 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ require 'test_case'
2
+
3
+ module Open4
4
+
5
+ class PFork4Test < TestCase
6
+ def test_fun_successful_return
7
+ fun = lambda { 'lucky me' }
8
+ cid, _ = pfork4 fun
9
+ assert_equal 0, wait_status(cid)
10
+ end
11
+
12
+ def test_fun_force_exit
13
+ exit_code = 43
14
+ fun = lambda { exit! exit_code }
15
+ cid, _ = pfork4 fun
16
+ assert_equal exit_code, wait_status(cid)
17
+ end
18
+
19
+ def test_fun_normal_exit
20
+ exit_code = 43
21
+ fun = lambda { exit exit_code }
22
+ cid, _ = pfork4 fun
23
+ assert_equal exit_code, wait_status(cid)
24
+ end
25
+
26
+ def test_fun_does_not_propagate_exception_without_block
27
+ fun = lambda { raise MyError }
28
+ cid, _ = pfork4 fun
29
+ refute_equal 0, wait_status(cid)
30
+ end
31
+
32
+ def test_fun_propagate_exception_with_block
33
+ fun = lambda { raise MyError }
34
+ assert_raises(MyError) { pfork4(fun) {} }
35
+ end
36
+
37
+ def test_fun_propagate_exception_with_block_avoids_zombie_child_process
38
+ fun = lambda { raise MyError }
39
+ assert_raises(MyError) { pfork4(fun) {} }
40
+ assert_empty Process.waitall
41
+ end
42
+
43
+ def test_call_block_upon_exception
44
+ fun = lambda { raise MyError }
45
+ block_called = false
46
+ assert_raises(MyError) { pfork4(fun) { block_called = true } }
47
+ assert_equal true, block_called
48
+ end
49
+
50
+ def test_passes_child_pid_to_block
51
+ fun = lambda { $stdout.write Process.pid }
52
+ cid_in_block = nil
53
+ cid_in_fun = nil
54
+ pfork4(fun) do |cid, _, stdout, _|
55
+ cid_in_block = cid
56
+ cid_in_fun = stdout.read.to_i
57
+ end
58
+ assert_equal cid_in_fun, cid_in_block
59
+ end
60
+
61
+ def test_io_pipes_without_block
62
+ via_msg = 'foo'
63
+ err_msg = 'bar'
64
+ fun = lambda do
65
+ $stdout.write $stdin.read
66
+ $stderr.write err_msg
67
+ end
68
+ out_actual, err_actual = nil, nil
69
+ cid, stdin, stdout, stderr = pfork4 fun
70
+ stdin.write via_msg
71
+ stdin.close
72
+ out_actual = stdout.read
73
+ err_actual = stderr.read
74
+ assert_equal via_msg, out_actual
75
+ assert_equal err_msg, err_actual
76
+ assert_equal 0, wait_status(cid)
77
+ end
78
+
79
+ def test_io_pipes_with_block
80
+ via_msg = 'foo'
81
+ err_msg = 'bar'
82
+ fun = lambda do
83
+ $stdout.write $stdin.read
84
+ $stderr.write err_msg
85
+ end
86
+ out_actual, err_actual = nil, nil
87
+ status = pfork4(fun) do |_, stdin, stdout, stderr|
88
+ stdin.write via_msg
89
+ stdin.close
90
+ out_actual = stdout.read
91
+ err_actual = stderr.read
92
+ end
93
+ assert_equal via_msg, out_actual
94
+ assert_equal err_msg, err_actual
95
+ assert_equal 0, status.exitstatus
96
+ end
97
+
98
+ def test_exec_in_fun
99
+ via_msg = 'foo'
100
+ fun = lambda { exec %{ruby -e "print '#{via_msg}'"} }
101
+ out_actual = nil
102
+ status = pfork4(fun) do |_, stdin, stdout, _|
103
+ stdin.close
104
+ out_actual = stdout.read
105
+ end
106
+ assert_equal via_msg, out_actual
107
+ assert_equal 0, status.exitstatus
108
+ end
109
+
110
+ def test_io_pipes_and_then_exception_propagation_with_block
111
+ via_msg = 'foo'
112
+ err_msg = 'bar'
113
+ fun = lambda do
114
+ $stdout.write $stdin.read
115
+ $stderr.write err_msg
116
+ raise MyError
117
+ end
118
+ out_actual, err_actual = nil, nil
119
+ assert_raises(MyError) do
120
+ pfork4(fun) do |_, stdin, stdout, stderr|
121
+ stdin.write via_msg
122
+ stdin.close
123
+ out_actual = stdout.read
124
+ err_actual = stderr.read
125
+ end
126
+ end
127
+ assert_equal via_msg, out_actual
128
+ assert_equal err_msg, err_actual
129
+ end
130
+
131
+ def test_blocked_on_io_read_and_exception_propagation_with_block
132
+ fun = lambda do
133
+ $stdin.read
134
+ raise MyError
135
+ end
136
+ out_actual, err_actual = nil, nil
137
+ assert_raises(MyError) do
138
+ pfork4(fun) do |_, stdin, stdout, stderr|
139
+ stdin.write 'foo'
140
+ stdin.close
141
+ out_actual = stdout.read
142
+ err_actual = stderr.read
143
+ end
144
+ end
145
+ assert_equal '', out_actual
146
+ assert_equal '', err_actual
147
+ end
148
+ end
149
+
150
+ end
@@ -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) { popen4 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) { popen4 UNKNOWN_CMD }
16
+ assert_empty Process.waitall
17
+ end
18
+
19
+ def test_exit_failure
20
+ code = 43
21
+ cid, _ = popen4 %{ruby -e "exit #{43}"}
22
+ assert_equal code, wait_status(cid)
23
+ end
24
+
25
+ def test_exit_success
26
+ cid, _ = popen4 %{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
+ popen4(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 = popen4 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 = popen4(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
@@ -0,0 +1,89 @@
1
+ require 'test_case'
2
+ require 'socket'
3
+
4
+ module Open4
5
+
6
+ class POpen4Test < TestCase
7
+ UNKNOWN_CMD = 'asdfadsfjlkkk'
8
+ UNKNOWN_CMD_ERRORS = [Errno::ENOENT, Errno::EINVAL]
9
+
10
+ def test_unknown_command_propagates_exception
11
+ err = assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
12
+ assert_match(/#{UNKNOWN_CMD}/, err.to_s) if on_mri?
13
+ end
14
+
15
+ def test_exception_propagation_avoids_zombie_child_process
16
+ assert_raises(*UNKNOWN_CMD_ERRORS) { popen4ext true, UNKNOWN_CMD }
17
+ assert_empty Process.waitall
18
+ end
19
+
20
+ def test_exit_failure
21
+ code = 43
22
+ cid, _ = popen4ext true, %{ruby -e "exit #{43}"}
23
+ assert_equal code, wait_status(cid)
24
+ end
25
+
26
+ def test_exit_success
27
+ cid, _ = popen4ext true, %{ruby -e "exit"}
28
+ assert_equal 0, wait_status(cid)
29
+ end
30
+
31
+ def test_passes_child_pid_to_block
32
+ cmd = %{ruby -e "STDOUT.print Process.pid"}
33
+ cid_in_block = nil
34
+ cid_in_fun = nil
35
+ popen4ext(true, cmd) do |cid, _, stdout, _|
36
+ cid_in_block = cid
37
+ cid_in_fun = stdout.read.to_i
38
+ end
39
+ assert_equal cid_in_fun, cid_in_block
40
+ end
41
+
42
+ def test_io_pipes_without_block
43
+ via_msg = 'foo'
44
+ err_msg = 'bar'
45
+ cmd = <<-END
46
+ ruby -e "
47
+ STDOUT.write STDIN.read
48
+ STDERR.write '#{err_msg}'
49
+ "
50
+ END
51
+ cid, stdin, stdout, stderr = popen4ext true, cmd
52
+ stdin.write via_msg
53
+ stdin.close
54
+ out_actual = stdout.read
55
+ err_actual = stderr.read
56
+ assert_equal via_msg, out_actual
57
+ assert_equal err_msg, err_actual
58
+ assert_equal 0, wait_status(cid)
59
+ end
60
+
61
+ def test_io_pipes_with_block
62
+ via_msg = 'foo'
63
+ err_msg = 'bar'
64
+ out_actual, err_actual = nil
65
+ cmd = <<-END
66
+ ruby -e "
67
+ STDOUT.write STDIN.read
68
+ STDERR.write '#{err_msg}'
69
+ "
70
+ END
71
+ status = popen4ext(true, cmd) do |_, stdin, stdout, stderr|
72
+ stdin.write via_msg
73
+ stdin.close
74
+ out_actual = stdout.read
75
+ err_actual = stderr.read
76
+ end
77
+ assert_equal via_msg, out_actual
78
+ assert_equal err_msg, err_actual
79
+ assert_equal 0, status.exitstatus
80
+ end
81
+
82
+ def test_close_ignores_errors
83
+ TCPSocket.new('localhost', 59367).close rescue nil
84
+ cid, _ = popen4ext true, %{ruby -e "exit"}
85
+ assert_equal 0, wait_status(cid)
86
+ end
87
+ end
88
+
89
+ end
metadata CHANGED
@@ -1,67 +1,64 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: open4
3
- version: !ruby/object:Gem::Version
4
- version: 1.0.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.4
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Ara T. Howard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2009-10-18 00:00:00 -06:00
13
- default_executable:
11
+ date: 2014-05-15 00:00:00.000000000 Z
14
12
  dependencies: []
15
-
16
- description: manage child processes and their io handles easily
13
+ description: ! 'open child process with handles on pid, stdin, stdout, and stderr:
14
+ manage child processes and their io handles easily.'
17
15
  email: ara.t.howard@gmail.com
18
16
  executables: []
19
-
20
17
  extensions: []
21
-
22
18
  extra_rdoc_files: []
23
-
24
- files:
25
- - lib/open4.rb
26
- - open4.gemspec
27
- - Rakefile
19
+ files:
20
+ - LICENSE
28
21
  - README
29
22
  - README.erb
23
+ - lib/open4.rb
24
+ - open4.gemspec
25
+ - rakefile
30
26
  - samples/bg.rb
31
27
  - samples/block.rb
32
28
  - samples/exception.rb
29
+ - samples/jesse-caldwell.rb
30
+ - samples/pfork4.rb
33
31
  - samples/simple.rb
34
32
  - samples/spawn.rb
35
33
  - samples/stdin_timeout.rb
36
34
  - samples/timeout.rb
35
+ - test/lib/test_case.rb
36
+ - test/pfork4_test.rb
37
+ - test/popen4_test.rb
38
+ - test/popen4ext_test.rb
37
39
  - white_box/leak.rb
38
- has_rdoc: true
39
- homepage: http://github.com/ahoward/open4/tree/master
40
- licenses: []
41
-
40
+ homepage: https://github.com/ahoward/open4
41
+ licenses:
42
+ - Ruby
43
+ metadata: {}
42
44
  post_install_message:
43
45
  rdoc_options: []
44
-
45
- require_paths:
46
+ require_paths:
46
47
  - lib
47
- required_ruby_version: !ruby/object:Gem::Requirement
48
- requirements:
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- version: "0"
52
- version:
53
- required_rubygems_version: !ruby/object:Gem::Requirement
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: "0"
58
- version:
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
59
58
  requirements: []
60
-
61
59
  rubyforge_project: codeforpeople
62
- rubygems_version: 1.3.5
60
+ rubygems_version: 2.0.3
63
61
  signing_key:
64
- specification_version: 3
62
+ specification_version: 4
65
63
  summary: open4
66
64
  test_files: []
67
-
data/Rakefile DELETED
@@ -1,225 +0,0 @@
1
-
2
- This.rubyforge_project = 'codeforpeople'
3
- This.author = "Ara T. Howard"
4
- This.email = "ara.t.howard@gmail.com"
5
- This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
6
-
7
-
8
- task :default do
9
- puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
10
- end
11
-
12
-
13
- task :gemspec do
14
- ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
15
- ignore_directories = 'pkg'
16
-
17
- shiteless =
18
- lambda do |list|
19
- list.delete_if do |entry|
20
- next unless test(?e, entry)
21
- extension = File.basename(entry).split(%r/[.]/).last
22
- ignore_extensions.any?{|ext| ext === extension}
23
- end
24
- list.delete_if do |entry|
25
- next unless test(?d, entry)
26
- dirname = File.expand_path(entry)
27
- ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
28
- end
29
- end
30
-
31
- lib = This.lib
32
- version = This.version
33
- files = shiteless[Dir::glob("**/**")]
34
- executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
35
- has_rdoc = true #File.exist?('doc')
36
- test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
37
-
38
- extensions = This.extensions
39
- if extensions.nil?
40
- %w( Makefile configure extconf.rb ).each do |ext|
41
- extensions << ext if File.exists?(ext)
42
- end
43
- end
44
- extensions = [extensions].flatten.compact
45
-
46
- template =
47
- if test(?e, 'gemspec.erb')
48
- Template{ IO.read('gemspec.erb') }
49
- else
50
- Template {
51
- <<-__
52
- ## #{ lib }.gemspec
53
- #
54
-
55
- Gem::Specification::new do |spec|
56
- spec.name = #{ lib.inspect }
57
- spec.version = #{ version.inspect }
58
- spec.platform = Gem::Platform::RUBY
59
- spec.summary = #{ lib.inspect }
60
-
61
- spec.description = "manage child processes and their io handles easily"
62
-
63
- spec.files = #{ files.inspect }
64
- spec.executables = #{ executables.inspect }
65
-
66
- spec.require_path = "lib"
67
-
68
- spec.has_rdoc = #{ has_rdoc.inspect }
69
- spec.test_files = #{ test_files.inspect }
70
- #spec.add_dependency 'lib', '>= version'
71
- #spec.add_dependency 'fattr'
72
-
73
- spec.extensions.push(*#{ extensions.inspect })
74
-
75
- spec.rubyforge_project = #{ This.rubyforge_project.inspect }
76
- spec.author = #{ This.author.inspect }
77
- spec.email = #{ This.email.inspect }
78
- spec.homepage = #{ This.homepage.inspect }
79
- end
80
- __
81
- }
82
- end
83
-
84
- open("#{ lib }.gemspec", "w"){|fd| fd.puts template}
85
- This.gemspec = "#{ lib }.gemspec"
86
- end
87
-
88
- task :gem => [:clean, :gemspec] do
89
- Fu.mkdir_p This.pkgdir
90
- before = Dir['*.gem']
91
- cmd = "gem build #{ This.gemspec }"
92
- `#{ cmd }`
93
- after = Dir['*.gem']
94
- gem = ((after - before).first || after.first) or abort('no gem!')
95
- Fu.mv gem, This.pkgdir
96
- This.gem = File.basename(gem)
97
- end
98
-
99
-
100
- task :readme do
101
- samples = ''
102
- prompt = '~ > '
103
- lib = This.lib
104
- version = This.version
105
-
106
- Dir['sample*/*'].sort.each do |sample|
107
- samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
108
-
109
- cmd = "cat #{ sample }"
110
- samples << Util.indent(prompt + cmd, 2) << "\n\n"
111
- samples << Util.indent(`#{ cmd }`, 4) << "\n"
112
-
113
- cmd = "ruby #{ sample }"
114
- samples << Util.indent(prompt + cmd, 2) << "\n\n"
115
-
116
- cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'"
117
- samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
118
- end
119
-
120
- template =
121
- if test(?e, 'readme.erb')
122
- Template{ IO.read('readme.erb') }
123
- else
124
- Template {
125
- <<-__
126
- NAME
127
- #{ lib }
128
-
129
- DESCRIPTION
130
-
131
- INSTALL
132
- gem install #{ lib }
133
-
134
- SAMPLES
135
- #{ samples }
136
- __
137
- }
138
- end
139
-
140
- open("README", "w"){|fd| fd.puts template}
141
- end
142
-
143
-
144
- task :clean do
145
- Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
146
- end
147
-
148
-
149
- task :release => [:clean, :gemspec, :gem] do
150
- gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
151
- raise "which one? : #{ gems.inspect }" if gems.size > 1
152
- raise "no gems?" if gems.size < 1
153
- cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }"
154
- puts cmd
155
- system cmd
156
- end
157
-
158
-
159
-
160
-
161
-
162
- BEGIN {
163
- $VERBOSE = nil
164
-
165
- require 'ostruct'
166
- require 'erb'
167
- require 'fileutils'
168
-
169
- Fu = FileUtils
170
-
171
- This = OpenStruct.new
172
-
173
- This.file = File.expand_path(__FILE__)
174
- This.dir = File.dirname(This.file)
175
- This.pkgdir = File.join(This.dir, 'pkg')
176
-
177
- lib = ENV['LIB']
178
- unless lib
179
- lib = File.basename(Dir.pwd)
180
- end
181
- This.lib = lib
182
-
183
- version = ENV['VERSION']
184
- unless version
185
- name = lib.capitalize
186
- require "./lib/#{ lib }"
187
- version = eval(name).send(:version)
188
- end
189
- This.version = version
190
-
191
- abort('no lib') unless This.lib
192
- abort('no version') unless This.version
193
-
194
- module Util
195
- def indent(s, n = 2)
196
- s = unindent(s)
197
- ws = ' ' * n
198
- s.gsub(%r/^/, ws)
199
- end
200
-
201
- def unindent(s)
202
- indent = nil
203
- s.each do |line|
204
- next if line =~ %r/^\s*$/
205
- indent = line[%r/^\s*/] and break
206
- end
207
- indent ? s.gsub(%r/^#{ indent }/, "") : s
208
- end
209
- extend self
210
- end
211
-
212
- class Template
213
- def initialize(&block)
214
- @block = block
215
- @template = block.call.to_s
216
- end
217
- def expand(b=nil)
218
- ERB.new(Util.unindent(@template)).result(b||@block)
219
- end
220
- alias_method 'to_s', 'expand'
221
- end
222
- def Template(*args, &block) Template.new(*args, &block) end
223
-
224
- Dir.chdir(This.dir)
225
- }