open4 1.0.1 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- }