childprocess 0.9.0 → 3.0.0

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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.document +6 -6
  3. data/.gitignore +28 -28
  4. data/.rspec +1 -1
  5. data/.travis.yml +40 -44
  6. data/CHANGELOG.md +73 -49
  7. data/Gemfile +21 -15
  8. data/LICENSE +20 -20
  9. data/README.md +218 -196
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +42 -60
  12. data/childprocess.gemspec +26 -30
  13. data/lib/childprocess.rb +210 -205
  14. data/lib/childprocess/abstract_io.rb +36 -36
  15. data/lib/childprocess/abstract_process.rb +192 -192
  16. data/lib/childprocess/errors.rb +37 -26
  17. data/lib/childprocess/jruby.rb +56 -56
  18. data/lib/childprocess/jruby/io.rb +16 -16
  19. data/lib/childprocess/jruby/process.rb +184 -159
  20. data/lib/childprocess/jruby/pump.rb +53 -53
  21. data/lib/childprocess/tools/generator.rb +145 -145
  22. data/lib/childprocess/unix.rb +9 -9
  23. data/lib/childprocess/unix/fork_exec_process.rb +78 -70
  24. data/lib/childprocess/unix/io.rb +21 -21
  25. data/lib/childprocess/unix/lib.rb +186 -186
  26. data/lib/childprocess/unix/platform/i386-linux.rb +12 -12
  27. data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
  28. data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
  29. data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
  30. data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
  31. data/lib/childprocess/unix/process.rb +90 -89
  32. data/lib/childprocess/version.rb +3 -3
  33. data/lib/childprocess/windows.rb +38 -33
  34. data/lib/childprocess/windows/handle.rb +91 -91
  35. data/lib/childprocess/windows/io.rb +25 -25
  36. data/lib/childprocess/windows/lib.rb +416 -416
  37. data/lib/childprocess/windows/process.rb +130 -130
  38. data/lib/childprocess/windows/process_builder.rb +178 -175
  39. data/lib/childprocess/windows/structs.rb +148 -148
  40. data/spec/abstract_io_spec.rb +12 -12
  41. data/spec/childprocess_spec.rb +447 -422
  42. data/spec/get_env.ps1 +13 -0
  43. data/spec/io_spec.rb +228 -228
  44. data/spec/jruby_spec.rb +24 -24
  45. data/spec/pid_behavior.rb +12 -12
  46. data/spec/platform_detection_spec.rb +86 -86
  47. data/spec/spec_helper.rb +270 -261
  48. data/spec/unix_spec.rb +57 -57
  49. data/spec/windows_spec.rb +23 -23
  50. metadata +8 -39
@@ -1,24 +1,24 @@
1
- require File.expand_path('../spec_helper', __FILE__)
2
- require "pid_behavior"
3
-
4
- if ChildProcess.jruby? && !ChildProcess.windows?
5
- describe ChildProcess::JRuby::IO do
6
- let(:io) { ChildProcess::JRuby::IO.new }
7
-
8
- it "raises an ArgumentError if given IO does not respond to :to_outputstream" do
9
- expect { io.stdout = nil }.to raise_error(ArgumentError)
10
- end
11
- end
12
-
13
- describe ChildProcess::JRuby::Process do
14
- if ChildProcess.unix?
15
- it_behaves_like "a platform that provides the child's pid"
16
- else
17
- it "raises an error when trying to access the child's pid" do
18
- process = exit_with(0)
19
- process.start
20
- expect { process.pid }.to raise_error(NotImplementedError)
21
- end
22
- end
23
- end
24
- end
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require "pid_behavior"
3
+
4
+ if ChildProcess.jruby? && !ChildProcess.windows?
5
+ describe ChildProcess::JRuby::IO do
6
+ let(:io) { ChildProcess::JRuby::IO.new }
7
+
8
+ it "raises an ArgumentError if given IO does not respond to :to_outputstream" do
9
+ expect { io.stdout = nil }.to raise_error(ArgumentError)
10
+ end
11
+ end
12
+
13
+ describe ChildProcess::JRuby::Process do
14
+ if ChildProcess.unix?
15
+ it_behaves_like "a platform that provides the child's pid"
16
+ else
17
+ it "raises an error when trying to access the child's pid" do
18
+ process = exit_with(0)
19
+ process.start
20
+ expect { process.pid }.to raise_error(NotImplementedError)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,12 +1,12 @@
1
- require File.expand_path('../spec_helper', __FILE__)
2
-
3
- shared_examples_for "a platform that provides the child's pid" do
4
- it "knows the child's pid" do
5
- Tempfile.open("pid-spec") do |file|
6
- process = write_pid(file.path).start
7
- process.wait
8
-
9
- expect(process.pid).to eq rewind_and_read(file).chomp.to_i
10
- end
11
- end
12
- end
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ shared_examples_for "a platform that provides the child's pid" do
4
+ it "knows the child's pid" do
5
+ Tempfile.open("pid-spec") do |file|
6
+ process = write_pid(file.path).start
7
+ process.wait
8
+
9
+ expect(process.pid).to eq rewind_and_read(file).chomp.to_i
10
+ end
11
+ end
12
+ end
@@ -1,86 +1,86 @@
1
- require File.expand_path('../spec_helper', __FILE__)
2
-
3
- # Q: Should platform detection concern be extracted from ChildProcess?
4
- describe ChildProcess do
5
-
6
- describe ".arch" do
7
- subject { described_class.arch }
8
-
9
- before(:each) { described_class.instance_variable_set(:@arch, nil) }
10
-
11
- after(:each) { described_class.instance_variable_set(:@arch, nil) }
12
-
13
- shared_examples 'expected_arch_for_host_cpu' do |host_cpu, expected_arch|
14
- context "when host_cpu is '#{host_cpu}'" do
15
- before :each do
16
- allow(RbConfig::CONFIG).
17
- to receive(:[]).
18
- with('host_cpu').
19
- and_return(expected_arch)
20
- end
21
-
22
- it { is_expected.to eq expected_arch }
23
- end
24
- end
25
-
26
- # Normal cases: not macosx - depends only on host_cpu
27
- context "when os is *not* 'macosx'" do
28
- before :each do
29
- allow(described_class).to receive(:os).and_return(:not_macosx)
30
- end
31
-
32
- [
33
- { host_cpu: 'i386', expected_arch: 'i386' },
34
- { host_cpu: 'i486', expected_arch: 'i386' },
35
- { host_cpu: 'i586', expected_arch: 'i386' },
36
- { host_cpu: 'i686', expected_arch: 'i386' },
37
- { host_cpu: 'amd64', expected_arch: 'x86_64' },
38
- { host_cpu: 'x86_64', expected_arch: 'x86_64' },
39
- { host_cpu: 'ppc', expected_arch: 'powerpc' },
40
- { host_cpu: 'powerpc', expected_arch: 'powerpc' },
41
- { host_cpu: 'unknown', expected_arch: 'unknown' },
42
- ].each do |args|
43
- include_context 'expected_arch_for_host_cpu', args.values
44
- end
45
- end
46
-
47
- # Special cases: macosx - when host_cpu is i686, have to re-check
48
- context "when os is 'macosx'" do
49
- before :each do
50
- allow(described_class).to receive(:os).and_return(:macosx)
51
- end
52
-
53
- context "when host_cpu is 'i686' " do
54
- shared_examples 'expected_arch_on_macosx_i686' do |is_64, expected_arch|
55
- context "when Ruby is #{is_64 ? 64 : 32}-bit" do
56
- before :each do
57
- allow(described_class).
58
- to receive(:is_64_bit?).
59
- and_return(is_64)
60
- end
61
-
62
- include_context 'expected_arch_for_host_cpu', 'i686', expected_arch
63
- end
64
- end
65
-
66
- [
67
- { is_64: true, expected_arch: 'x86_64' },
68
- { is_64: false, expected_arch: 'i386' }
69
- ].each do |args|
70
- include_context 'expected_arch_on_macosx_i686', args.values
71
- end
72
- end
73
-
74
- [
75
- { host_cpu: 'amd64', expected_arch: 'x86_64' },
76
- { host_cpu: 'x86_64', expected_arch: 'x86_64' },
77
- { host_cpu: 'ppc', expected_arch: 'powerpc' },
78
- { host_cpu: 'powerpc', expected_arch: 'powerpc' },
79
- { host_cpu: 'unknown', expected_arch: 'unknown' },
80
- ].each do |args|
81
- include_context 'expected_arch_for_host_cpu', args.values
82
- end
83
- end
84
- end
85
-
86
- end
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ # Q: Should platform detection concern be extracted from ChildProcess?
4
+ describe ChildProcess do
5
+
6
+ describe ".arch" do
7
+ subject { described_class.arch }
8
+
9
+ before(:each) { described_class.instance_variable_set(:@arch, nil) }
10
+
11
+ after(:each) { described_class.instance_variable_set(:@arch, nil) }
12
+
13
+ shared_examples 'expected_arch_for_host_cpu' do |host_cpu, expected_arch|
14
+ context "when host_cpu is '#{host_cpu}'" do
15
+ before :each do
16
+ allow(RbConfig::CONFIG).
17
+ to receive(:[]).
18
+ with('host_cpu').
19
+ and_return(expected_arch)
20
+ end
21
+
22
+ it { is_expected.to eq expected_arch }
23
+ end
24
+ end
25
+
26
+ # Normal cases: not macosx - depends only on host_cpu
27
+ context "when os is *not* 'macosx'" do
28
+ before :each do
29
+ allow(described_class).to receive(:os).and_return(:not_macosx)
30
+ end
31
+
32
+ [
33
+ { host_cpu: 'i386', expected_arch: 'i386' },
34
+ { host_cpu: 'i486', expected_arch: 'i386' },
35
+ { host_cpu: 'i586', expected_arch: 'i386' },
36
+ { host_cpu: 'i686', expected_arch: 'i386' },
37
+ { host_cpu: 'amd64', expected_arch: 'x86_64' },
38
+ { host_cpu: 'x86_64', expected_arch: 'x86_64' },
39
+ { host_cpu: 'ppc', expected_arch: 'powerpc' },
40
+ { host_cpu: 'powerpc', expected_arch: 'powerpc' },
41
+ { host_cpu: 'unknown', expected_arch: 'unknown' },
42
+ ].each do |args|
43
+ include_context 'expected_arch_for_host_cpu', args.values
44
+ end
45
+ end
46
+
47
+ # Special cases: macosx - when host_cpu is i686, have to re-check
48
+ context "when os is 'macosx'" do
49
+ before :each do
50
+ allow(described_class).to receive(:os).and_return(:macosx)
51
+ end
52
+
53
+ context "when host_cpu is 'i686' " do
54
+ shared_examples 'expected_arch_on_macosx_i686' do |is_64, expected_arch|
55
+ context "when Ruby is #{is_64 ? 64 : 32}-bit" do
56
+ before :each do
57
+ allow(described_class).
58
+ to receive(:is_64_bit?).
59
+ and_return(is_64)
60
+ end
61
+
62
+ include_context 'expected_arch_for_host_cpu', 'i686', expected_arch
63
+ end
64
+ end
65
+
66
+ [
67
+ { is_64: true, expected_arch: 'x86_64' },
68
+ { is_64: false, expected_arch: 'i386' }
69
+ ].each do |args|
70
+ include_context 'expected_arch_on_macosx_i686', args.values
71
+ end
72
+ end
73
+
74
+ [
75
+ { host_cpu: 'amd64', expected_arch: 'x86_64' },
76
+ { host_cpu: 'x86_64', expected_arch: 'x86_64' },
77
+ { host_cpu: 'ppc', expected_arch: 'powerpc' },
78
+ { host_cpu: 'powerpc', expected_arch: 'powerpc' },
79
+ { host_cpu: 'unknown', expected_arch: 'unknown' },
80
+ ].each do |args|
81
+ include_context 'expected_arch_for_host_cpu', args.values
82
+ end
83
+ end
84
+ end
85
+
86
+ end
@@ -1,261 +1,270 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
-
4
- unless defined?(JRUBY_VERSION)
5
- require 'coveralls'
6
- Coveralls.wear!
7
- end
8
-
9
- require 'childprocess'
10
- require 'rspec'
11
- require 'tempfile'
12
- require 'socket'
13
- require 'stringio'
14
- require 'ostruct'
15
-
16
- module ChildProcessSpecHelper
17
- RUBY = defined?(Gem) ? Gem.ruby : 'ruby'
18
-
19
- def ruby_process(*args)
20
- @process = ChildProcess.build(RUBY , *args)
21
- end
22
-
23
- def sleeping_ruby(seconds = nil)
24
- if seconds
25
- ruby_process("-e", "sleep #{seconds}")
26
- else
27
- ruby_process("-e", "sleep")
28
- end
29
- end
30
-
31
- def invalid_process
32
- @process = ChildProcess.build("unlikelytoexist")
33
- end
34
-
35
- def ignored(signal)
36
- code = <<-RUBY
37
- trap(#{signal.inspect}, "IGNORE")
38
- sleep
39
- RUBY
40
-
41
- ruby_process tmp_script(code)
42
- end
43
-
44
- def write_env(path)
45
- code = <<-RUBY
46
- File.open(#{path.inspect}, "w") { |f| f << ENV.inspect }
47
- RUBY
48
-
49
- ruby_process tmp_script(code)
50
- end
51
-
52
- def write_argv(path, *args)
53
- code = <<-RUBY
54
- File.open(#{path.inspect}, "w") { |f| f << ARGV.inspect }
55
- RUBY
56
-
57
- ruby_process(tmp_script(code), *args)
58
- end
59
-
60
- def write_pid(path)
61
- code = <<-RUBY
62
- File.open(#{path.inspect}, "w") { |f| f << Process.pid }
63
- RUBY
64
-
65
- ruby_process tmp_script(code)
66
- end
67
-
68
- def write_pid_in_sleepy_grand_child(path)
69
- code = <<-RUBY
70
- system "ruby", "-e", 'File.open(#{path.inspect}, "w") { |f| f << Process.pid; f.flush }; sleep'
71
- RUBY
72
-
73
- ruby_process tmp_script(code)
74
- end
75
-
76
- def exit_with(exit_code)
77
- ruby_process(tmp_script("exit(#{exit_code})"))
78
- end
79
-
80
- def with_env(hash)
81
- hash.each { |k,v| ENV[k] = v }
82
- begin
83
- yield
84
- ensure
85
- hash.each_key { |k| ENV[k] = nil }
86
- end
87
- end
88
-
89
- def tmp_script(code)
90
- # use an ivar to avoid GC
91
- @tf = Tempfile.new("childprocess-temp")
92
- @tf << code
93
- @tf.close
94
-
95
- puts code if $DEBUG
96
-
97
- @tf.path
98
- end
99
-
100
- def cat
101
- if ChildProcess.os == :windows
102
- ruby(<<-CODE)
103
- STDIN.sync = STDOUT.sync = true
104
- IO.copy_stream(STDIN, STDOUT)
105
- CODE
106
- else
107
- ChildProcess.build("cat")
108
- end
109
- end
110
-
111
- def echo
112
- if ChildProcess.os == :windows
113
- ruby(<<-CODE)
114
- STDIN.sync = true
115
- STDOUT.sync = true
116
-
117
- puts "hello"
118
- CODE
119
- else
120
- ChildProcess.build("echo", "hello")
121
- end
122
- end
123
-
124
- def ruby(code)
125
- ruby_process(tmp_script(code))
126
- end
127
-
128
- def with_executable_at(path, &blk)
129
- if ChildProcess.os == :windows
130
- path << ".cmd"
131
- content = "#{RUBY} -e 'sleep 10' \n @echo foo"
132
- else
133
- content = "#!/bin/sh\nsleep 10\necho foo"
134
- end
135
-
136
- File.open(path, 'w', 0744) { |io| io << content }
137
- proc = ChildProcess.build(path)
138
-
139
- begin
140
- yield proc
141
- ensure
142
- proc.stop if proc.alive?
143
- File.delete path
144
- end
145
- end
146
-
147
- def exit_timeout
148
- 10
149
- end
150
-
151
- def random_free_port
152
- server = TCPServer.new('127.0.0.1', 0)
153
- port = server.addr[1]
154
- server.close
155
-
156
- port
157
- end
158
-
159
- def with_tmpdir(&blk)
160
- name = "#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}"
161
- FileUtils.mkdir_p(name)
162
-
163
- begin
164
- yield File.expand_path(name)
165
- ensure
166
- FileUtils.rm_rf name
167
- end
168
- end
169
-
170
- def wait_until(timeout = 10, &blk)
171
- end_time = Time.now + timeout
172
- last_exception = nil
173
-
174
- until Time.now >= end_time
175
- begin
176
- result = yield
177
- return result if result
178
- rescue RSpec::Expectations::ExpectationNotMetError => ex
179
- last_exception = ex
180
- end
181
-
182
- sleep 0.01
183
- end
184
-
185
- msg = "timed out after #{timeout} seconds"
186
- msg << ":\n#{last_exception.message}" if last_exception
187
-
188
- raise msg
189
- end
190
-
191
- def can_bind?(host, port)
192
- TCPServer.new(host, port).close
193
- true
194
- rescue
195
- false
196
- end
197
-
198
- def rewind_and_read(io)
199
- io.rewind
200
- io.read
201
- end
202
-
203
- def alive?(pid)
204
- if ChildProcess.windows?
205
- ChildProcess::Windows::Lib.alive?(pid)
206
- else
207
- begin
208
- Process.getpgid pid
209
- true
210
- rescue Errno::ESRCH
211
- false
212
- end
213
- end
214
- end
215
-
216
- def capture_std
217
- orig_out = STDOUT.clone
218
- orig_err = STDERR.clone
219
-
220
- out = Tempfile.new 'captured-stdout'
221
- err = Tempfile.new 'captured-stderr'
222
- out.sync = true
223
- err.sync = true
224
-
225
- STDOUT.reopen out
226
- STDERR.reopen err
227
-
228
- yield
229
-
230
- OpenStruct.new stdout: rewind_and_read(out), stderr: rewind_and_read(err)
231
- ensure
232
- STDOUT.reopen orig_out
233
- STDERR.reopen orig_err
234
- end
235
-
236
- def generate_log_messages
237
- ChildProcess.logger.level = Logger::DEBUG
238
-
239
- process = exit_with(0).start
240
- process.wait
241
- process.poll_for_exit(0.1)
242
- end
243
-
244
- end # ChildProcessSpecHelper
245
-
246
- Thread.abort_on_exception = true
247
-
248
- RSpec.configure do |c|
249
- c.include(ChildProcessSpecHelper)
250
- c.after(:each) {
251
- defined?(@process) && @process.alive? && @process.stop
252
- }
253
-
254
- if ChildProcess.jruby? && ChildProcess.new("true").instance_of?(ChildProcess::JRuby::Process)
255
- c.filter_run_excluding :process_builder => false
256
- end
257
-
258
- if ChildProcess.linux? && ChildProcess.posix_spawn?
259
- c.filter_run_excluding :posix_spawn_on_linux => false
260
- end
261
- end
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ unless defined?(JRUBY_VERSION)
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+ end
8
+
9
+ require 'childprocess'
10
+ require 'rspec'
11
+ require 'tempfile'
12
+ require 'socket'
13
+ require 'stringio'
14
+ require 'ostruct'
15
+
16
+ module ChildProcessSpecHelper
17
+ RUBY = defined?(Gem) ? Gem.ruby : 'ruby'
18
+
19
+ def ruby_process(*args)
20
+ @process = ChildProcess.build(RUBY , *args)
21
+ end
22
+
23
+ def windows_process(*args)
24
+ @process = ChildProcess.build("powershell", *args)
25
+ end
26
+
27
+ def sleeping_ruby(seconds = nil)
28
+ if seconds
29
+ ruby_process("-e", "sleep #{seconds}")
30
+ else
31
+ ruby_process("-e", "sleep")
32
+ end
33
+ end
34
+
35
+ def invalid_process
36
+ @process = ChildProcess.build("unlikelytoexist")
37
+ end
38
+
39
+ def ignored(signal)
40
+ code = <<-RUBY
41
+ trap(#{signal.inspect}, "IGNORE")
42
+ sleep
43
+ RUBY
44
+
45
+ ruby_process tmp_script(code)
46
+ end
47
+
48
+ def write_env(path)
49
+ if ChildProcess.os == :windows
50
+ ps_env_file_path = File.expand_path(File.dirname(__FILE__))
51
+ args = ['-File', "#{ps_env_file_path}/get_env.ps1", path]
52
+ windows_process(*args)
53
+ else
54
+ code = <<-RUBY
55
+ File.open(#{path.inspect}, "w") { |f| f << ENV.inspect }
56
+ RUBY
57
+ ruby_process tmp_script(code)
58
+ end
59
+ end
60
+
61
+ def write_argv(path, *args)
62
+ code = <<-RUBY
63
+ File.open(#{path.inspect}, "w") { |f| f << ARGV.inspect }
64
+ RUBY
65
+
66
+ ruby_process(tmp_script(code), *args)
67
+ end
68
+
69
+ def write_pid(path)
70
+ code = <<-RUBY
71
+ File.open(#{path.inspect}, "w") { |f| f << Process.pid }
72
+ RUBY
73
+
74
+ ruby_process tmp_script(code)
75
+ end
76
+
77
+ def write_pid_in_sleepy_grand_child(path)
78
+ code = <<-RUBY
79
+ system "ruby", "-e", 'File.open(#{path.inspect}, "w") { |f| f << Process.pid; f.flush }; sleep'
80
+ RUBY
81
+
82
+ ruby_process tmp_script(code)
83
+ end
84
+
85
+ def exit_with(exit_code)
86
+ ruby_process(tmp_script("exit(#{exit_code})"))
87
+ end
88
+
89
+ def with_env(hash)
90
+ hash.each { |k,v| ENV[k] = v }
91
+ begin
92
+ yield
93
+ ensure
94
+ hash.each_key { |k| ENV[k] = nil }
95
+ end
96
+ end
97
+
98
+ def tmp_script(code)
99
+ # use an ivar to avoid GC
100
+ @tf = Tempfile.new("childprocess-temp")
101
+ @tf << code
102
+ @tf.close
103
+
104
+ puts code if $DEBUG
105
+
106
+ @tf.path
107
+ end
108
+
109
+ def cat
110
+ if ChildProcess.os == :windows
111
+ ruby(<<-CODE)
112
+ STDIN.sync = STDOUT.sync = true
113
+ IO.copy_stream(STDIN, STDOUT)
114
+ CODE
115
+ else
116
+ ChildProcess.build("cat")
117
+ end
118
+ end
119
+
120
+ def echo
121
+ if ChildProcess.os == :windows
122
+ ruby(<<-CODE)
123
+ STDIN.sync = true
124
+ STDOUT.sync = true
125
+
126
+ puts "hello"
127
+ CODE
128
+ else
129
+ ChildProcess.build("echo", "hello")
130
+ end
131
+ end
132
+
133
+ def ruby(code)
134
+ ruby_process(tmp_script(code))
135
+ end
136
+
137
+ def with_executable_at(path, &blk)
138
+ if ChildProcess.os == :windows
139
+ path << ".cmd"
140
+ content = "#{RUBY} -e 'sleep 10' \n @echo foo"
141
+ else
142
+ content = "#!/bin/sh\nsleep 10\necho foo"
143
+ end
144
+
145
+ File.open(path, 'w', 0744) { |io| io << content }
146
+ proc = ChildProcess.build(path)
147
+
148
+ begin
149
+ yield proc
150
+ ensure
151
+ proc.stop if proc.alive?
152
+ File.delete path
153
+ end
154
+ end
155
+
156
+ def exit_timeout
157
+ 10
158
+ end
159
+
160
+ def random_free_port
161
+ server = TCPServer.new('127.0.0.1', 0)
162
+ port = server.addr[1]
163
+ server.close
164
+
165
+ port
166
+ end
167
+
168
+ def with_tmpdir(&blk)
169
+ name = "#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}"
170
+ FileUtils.mkdir_p(name)
171
+
172
+ begin
173
+ yield File.expand_path(name)
174
+ ensure
175
+ FileUtils.rm_rf name
176
+ end
177
+ end
178
+
179
+ def wait_until(timeout = 10, &blk)
180
+ end_time = Time.now + timeout
181
+ last_exception = nil
182
+
183
+ until Time.now >= end_time
184
+ begin
185
+ result = yield
186
+ return result if result
187
+ rescue RSpec::Expectations::ExpectationNotMetError => ex
188
+ last_exception = ex
189
+ end
190
+
191
+ sleep 0.01
192
+ end
193
+
194
+ msg = "timed out after #{timeout} seconds"
195
+ msg << ":\n#{last_exception.message}" if last_exception
196
+
197
+ raise msg
198
+ end
199
+
200
+ def can_bind?(host, port)
201
+ TCPServer.new(host, port).close
202
+ true
203
+ rescue
204
+ false
205
+ end
206
+
207
+ def rewind_and_read(io)
208
+ io.rewind
209
+ io.read
210
+ end
211
+
212
+ def alive?(pid)
213
+ if ChildProcess.windows?
214
+ ChildProcess::Windows::Lib.alive?(pid)
215
+ else
216
+ begin
217
+ Process.getpgid pid
218
+ true
219
+ rescue Errno::ESRCH
220
+ false
221
+ end
222
+ end
223
+ end
224
+
225
+ def capture_std
226
+ orig_out = STDOUT.clone
227
+ orig_err = STDERR.clone
228
+
229
+ out = Tempfile.new 'captured-stdout'
230
+ err = Tempfile.new 'captured-stderr'
231
+ out.sync = true
232
+ err.sync = true
233
+
234
+ STDOUT.reopen out
235
+ STDERR.reopen err
236
+
237
+ yield
238
+
239
+ OpenStruct.new stdout: rewind_and_read(out), stderr: rewind_and_read(err)
240
+ ensure
241
+ STDOUT.reopen orig_out
242
+ STDERR.reopen orig_err
243
+ end
244
+
245
+ def generate_log_messages
246
+ ChildProcess.logger.level = Logger::DEBUG
247
+
248
+ process = exit_with(0).start
249
+ process.wait
250
+ process.poll_for_exit(0.1)
251
+ end
252
+
253
+ end # ChildProcessSpecHelper
254
+
255
+ Thread.abort_on_exception = true
256
+
257
+ RSpec.configure do |c|
258
+ c.include(ChildProcessSpecHelper)
259
+ c.after(:each) {
260
+ defined?(@process) && @process.alive? && @process.stop
261
+ }
262
+
263
+ if ChildProcess.jruby? && ChildProcess.new("true").instance_of?(ChildProcess::JRuby::Process)
264
+ c.filter_run_excluding :process_builder => false
265
+ end
266
+
267
+ if ChildProcess.linux? && ChildProcess.posix_spawn?
268
+ c.filter_run_excluding :posix_spawn_on_linux => false
269
+ end
270
+ end