childprocess 0.8.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 -36
  6. data/CHANGELOG.md +73 -44
  7. data/Gemfile +21 -15
  8. data/LICENSE +20 -20
  9. data/README.md +218 -192
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +42 -43
  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 -391
  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