right_popen 1.0.9 → 1.0.11

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.
data/README.rdoc CHANGED
@@ -26,6 +26,11 @@ to report issues.
26
26
  @stdout_text = ""
27
27
  @stderr_text = ""
28
28
  @exit_status = nil
29
+ @pid = nil
30
+
31
+ def on_pid(pid)
32
+ @pid = pid
33
+ end
29
34
 
30
35
  def on_read_stdout(data)
31
36
  @stdout_text << data
@@ -45,6 +50,7 @@ to report issues.
45
50
  RightScale.popen3(:command => command,
46
51
  :target => self,
47
52
  :environment => nil,
53
+ :pid_handler => :on_pid,
48
54
  :stdout_handler => :on_read_stdout,
49
55
  :stderr_handler => :on_read_stderr,
50
56
  :exit_handler => :on_exit)
@@ -60,6 +66,7 @@ to report issues.
60
66
  puts "@stdout_text = #{@stdout_text}"
61
67
  puts "@stderr_text = #{@stderr_text}"
62
68
  puts "@exit_status.exitstatus = #{@exit_status.exitstatus}"
69
+ puts "@pid = #{@pid}"
63
70
 
64
71
 
65
72
  == INSTALLATION
@@ -27,82 +27,27 @@
27
27
 
28
28
  require 'rubygems'
29
29
  require 'eventmachine'
30
+ require 'tempfile'
30
31
 
31
32
  module RightScale
32
-
33
- # Provides an eventmachine callback handler for the stdout stream.
34
- module StdOutHandler
35
-
36
- # === Parameters
37
- # options[:input](String):: Input to be sent to child process stdin
38
- # options[:target](Object):: Object defining handler methods to be called.
39
- # options[:stdout_handler(String):: Token for stdout handler method name.
40
- # options[:exit_handler(String):: Token for exit handler method name.
41
- # options[:exec_file](String):: Path to executed file
42
- # stderr_eventable(Connector):: EM object representing stderr handler.
43
- # read_fd(IO):: Standard output read file descriptor.
44
- # write_fd(IO):: Standard output write file descriptor.
45
- def initialize(options, stderr_eventable, read_fd, write_fd)
46
- @input = options[:input]
47
- @target = options[:target]
48
- @stdout_handler = options[:stdout_handler]
49
- @exit_handler = options[:exit_handler]
50
- @exec_file = options[:exec_file]
51
- @stderr_eventable = stderr_eventable
52
- # Just so they don't get GCed before the process goes away
53
- @read_fd = read_fd
54
- @write_fd = write_fd
55
- end
56
-
57
- # Send input to child process stdin
58
- def post_init
59
- send_data(@input) if @input
60
- end
61
-
62
- # Callback from EM to receive data.
63
- def receive_data(data)
64
- @target.method(@stdout_handler).call(data) if @stdout_handler
65
- end
66
-
67
- # Callback from EM to unbind.
68
- def unbind
69
- # We force the attached stderr handler to go away so that
70
- # we don't end up with a broken pipe
71
- File.delete(@exec_file) if File.file?(@exec_file)
72
- @stderr_eventable.force_detach if @stderr_eventable
73
- @target.method(@exit_handler).call(get_status) if @exit_handler
74
- end
75
- end
76
-
77
- module StdErrHandler
78
-
79
- # === Parameters
80
- # target(Object):: Object defining handler methods to be called.
81
- #
82
- # stderr_handler(String):: Token for stderr handler method name.
83
- # read_fd(IO):: Error output read file descriptor.
84
- def initialize(target, stderr_handler, read_fd)
33
+ module PipeHandler
34
+ def initialize(target, handler)
85
35
  @target = target
86
- @stderr_handler = stderr_handler
87
- @unbound = false
88
- @read_fd = read_fd # So it doesn't get GCed
36
+ @handler = handler
89
37
  end
90
38
 
91
- # Callback from EM to receive data.
92
39
  def receive_data(data)
93
- @target.method(@stderr_handler).call(data)
40
+ @target.method(@handler).call(data) if @handler
94
41
  end
95
-
96
- # Callback from EM to unbind.
97
- def unbind
98
- @unbound = true
42
+ end
43
+ module InputHandler
44
+ def initialize(string)
45
+ @string = string
99
46
  end
100
47
 
101
- # Forces detachment of the stderr handler on EM's next tick.
102
- def force_detach
103
- # Use next tick to prevent issue in EM where descriptors list
104
- # gets out-of-sync when calling detach in an unbind callback
105
- EM.next_tick { detach unless @unbound }
48
+ def post_init
49
+ send_data(@string) if @string
50
+ close_connection_after_writing
106
51
  end
107
52
  end
108
53
 
@@ -110,49 +55,64 @@ module RightScale
110
55
  # standard streams of the child process.
111
56
  #
112
57
  # === Parameters
58
+ # options[:pid_handler](Symbol):: Token for pid handler method name.
113
59
  # options[:temp_dir]:: Path to temporary directory where executable files are
114
60
  # created, default to /tmp if not specified
115
61
  #
116
62
  # See RightScale.popen3
117
63
  def self.popen3_imp(options)
118
- # First write command to file so that it's possible to use popen3 with
119
- # a bash command line (e.g. 'for i in 1 2 3 4 5; ...')
120
- temp_dir = options[:temp_dir] || '/tmp'
121
- exec_file = File.join(temp_dir, Time.new.to_i.to_s)
122
- options[:exec_file] = exec_file
123
- File.open(exec_file, 'w') { |f| f.puts options[:command] }
124
- File.chmod(0700, exec_file)
125
64
  GC.start # To garbage collect open file descriptors from passed executions
126
65
  EM.next_tick do
127
- saved_stderr = $stderr.dup
128
- r, w = Socket::pair(Socket::AF_LOCAL, Socket::SOCK_STREAM, 0)#IO::pipe
129
-
130
- $stderr.reopen w
131
- c = EM.attach(r, StdErrHandler, options[:target], options[:stderr_handler], r) if options[:stderr_handler]
132
-
133
- # Setup environment for child process
134
- envs = {}
135
- options[:environment].each { |k, v| envs[k.to_s] = v } if options[:environment]
136
- unless envs.empty?
137
- old_envs = {}
138
- ENV.each { |k, v| old_envs[k] = v if envs.include?(k) }
139
- envs.each { |k, v| ENV[k] = v }
66
+ inr, inw = IO::pipe
67
+ outr, outw = IO::pipe
68
+ errr, errw = IO::pipe
69
+
70
+ [inr, inw, outr, outw, errr, errw].each {|fdes| fdes.sync = true}
71
+
72
+ pid = fork do
73
+ options[:environment].each do |k, v|
74
+ ENV[k.to_s] = v
75
+ end unless options[:environment].nil?
76
+
77
+ inw.close
78
+ outr.close
79
+ errr.close
80
+ $stdin.reopen inr
81
+ $stdout.reopen outw
82
+ $stderr.reopen errw
83
+
84
+ if options[:command].instance_of?(String)
85
+ exec "sh", "-c", options[:command]
86
+ else
87
+ exec *options[:command]
88
+ end
140
89
  end
141
90
 
142
- # Launch child process
143
- EM.popen(exec_file, StdOutHandler, options, c, r, w)
144
-
145
- # Restore environment variables
146
- unless envs.empty?
147
- envs.each { |k, _| ENV[k] = nil }
148
- old_envs.each { |k, v| ENV[k] = v }
91
+ inr.close
92
+ outw.close
93
+ errw.close
94
+ stderr = EM.attach(errr, PipeHandler, options[:target],
95
+ options[:stderr_handler])
96
+ stdout = EM.attach(outr, PipeHandler, options[:target],
97
+ options[:stdout_handler])
98
+ stdin = EM.attach(inw, InputHandler, options[:input])
99
+
100
+ options[:target].method(options[:pid_handler]).call(pid) if
101
+ options.key? :pid_handler
102
+
103
+ wait_timer = EM::PeriodicTimer.new(1) do
104
+ value = Process.waitpid2(pid, Process::WNOHANG)
105
+ unless value.nil?
106
+ ignored, status = value
107
+ wait_timer.cancel
108
+ stdin.close_connection
109
+ stdout.close_connection
110
+ stderr.close_connection
111
+ options[:target].method(options[:exit_handler]).call(status) if
112
+ options[:exit_handler]
113
+ end
149
114
  end
150
-
151
- # Do not close 'w', strange things happen otherwise
152
- # (command protocol socket gets closed during decommission)
153
- $stderr.reopen saved_stderr
154
115
  end
155
116
  true
156
117
  end
157
-
158
118
  end
data/lib/right_popen.rb CHANGED
@@ -45,10 +45,11 @@ module RightScale
45
45
  # All handlers must be methods exposed by the given target.
46
46
  #
47
47
  # === Parameters
48
- # options[:command](String):: Command to execute, including any arguments
48
+ # options[:command](String or Array):: Command to execute, including any arguments as a single string or an array of command and arguments
49
49
  # options[:environment](Hash):: Hash of environment variables values keyed by name
50
50
  # options[:input](String):: Input string that will get streamed into child's process stdin
51
51
  # options[:target](Object):: object defining handler methods to be called, optional (no handlers can be defined if not specified)
52
+ # options[:pid_handler](String):: PID notification handler method name, optional
52
53
  # options[:stdout_handler](String):: Stdout handler method name, optional
53
54
  # options[:stderr_handler](String):: Stderr handler method name, optional
54
55
  # options[:exit_handler](String):: Exit handler method name, optional
@@ -58,7 +59,7 @@ module RightScale
58
59
  def self.popen3(options)
59
60
  raise "EventMachine reactor must be started" unless EM.reactor_running?
60
61
  raise "Missing command" unless options[:command]
61
- raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler]
62
+ raise "Missing target" unless options[:target] || !options[:stdout_handler] && !options[:stderr_handler] && !options[:exit_handler] && !options[:pid_handler]
62
63
  RightScale.popen3_imp(options)
63
64
  true
64
65
  end
data/right_popen.gemspec CHANGED
@@ -6,8 +6,8 @@ end
6
6
 
7
7
  spec = Gem::Specification.new do |spec|
8
8
  spec.name = 'right_popen'
9
- spec.version = '1.0.9'
10
- spec.authors = ['Scott Messier', 'Raphael Simon']
9
+ spec.version = '1.0.11'
10
+ spec.authors = ['Scott Messier', 'Raphael Simon', 'Graham Hughes']
11
11
  spec.email = 'scott@rightscale.com'
12
12
  spec.homepage = 'https://github.com/rightscale/right_popen'
13
13
  if is_windows?
@@ -1,182 +1,233 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
- RUBY_CMD = 'ruby'
4
- STANDARD_MESSAGE = 'Standard message'
5
- ERROR_MESSAGE = 'Error message'
6
- EXIT_STATUS = 146
3
+ describe 'RightScale::popen3' do
7
4
 
8
- # manually bump count up for more aggressive multi-processor testing, lessen
9
- # for a quick smoke test
10
- LARGE_OUTPUT_COUNTER = 1000
5
+ module RightPopenSpec
6
+ class Runner
7
+ class RunnerStatus
8
+ def initialize(command, block)
9
+ @output_text = ""
10
+ @error_text = ""
11
+ @status = nil
12
+ @did_timeout = false
13
+ @callback = block
14
+ @pid = nil
15
+ EM.next_tick do
16
+ @timeout = EM::Timer.new(2) do
17
+ puts "\n** Failed to run #{command.inspect}: Timeout"
18
+ @did_timeout = true
19
+ @callback.call(self)
20
+ end
21
+ end
22
+ end
11
23
 
12
- # bump up count for most exhaustive leak detection.
13
- REPEAT_TEST_COUNTER = 256
24
+ attr_accessor :output_text, :error_text, :status, :did_timeout, :pid
14
25
 
15
- def is_windows?
16
- return RUBY_PLATFORM =~ /mswin/
17
- end
26
+ def on_read_stdout(data)
27
+ @output_text << data
28
+ end
18
29
 
19
- describe 'RightScale::popen3' do
30
+ def on_read_stderr(data)
31
+ @error_text << data
32
+ end
20
33
 
21
- module RightPopenSpec
34
+ def on_pid(pid)
35
+ raise "PID already set!" unless @pid.nil?
36
+ @pid = pid
37
+ end
38
+
39
+ def on_exit(status)
40
+ @timeout.cancel if @timeout
41
+ @status = status
42
+ @callback.call(self)
43
+ end
44
+ end
22
45
 
23
- class Runner
24
46
  def initialize
47
+ @count = 0
25
48
  @done = false
26
- @output_text = nil
27
- @error_text = nil
28
- @status = nil
29
49
  @last_exception = nil
30
50
  @last_iteration = 0
31
51
  end
32
52
 
33
- attr_reader :output_text, :error_text, :status
34
-
35
- def do_right_popen(command, env=nil, input=nil)
36
- @timeout = EM::Timer.new(2) { puts "\n** Failed to run #{command.inspect}: Timeout"; EM.stop }
37
- @output_text = ''
38
- @error_text = ''
39
- @status = nil
40
- RightScale.popen3(:command => command,
53
+ def do_right_popen(command, env=nil, input=nil, &callback)
54
+ status = RunnerStatus.new(command, callback)
55
+ RightScale.popen3(:command => command,
41
56
  :input => input,
42
- :target => self,
57
+ :target => status,
43
58
  :environment => env,
44
- :stdout_handler => :on_read_stdout,
45
- :stderr_handler => :on_read_stderr,
59
+ :stdout_handler => :on_read_stdout,
60
+ :stderr_handler => :on_read_stderr,
61
+ :pid_handler => :on_pid,
46
62
  :exit_handler => :on_exit)
63
+ status
47
64
  end
48
65
 
49
66
  def run_right_popen(command, env=nil, input=nil, count=1)
50
67
  begin
51
- @command = command
52
- @env = env
53
- @last_iteration = 0
54
- @count = count
55
- puts "#{count}>" if count > 1
56
- EM.run { EM.next_tick { do_right_popen(command, env, input) } }
68
+ @iterations = 0
69
+ EM.run do
70
+ EM.next_tick do
71
+ do_right_popen(command, env, input) do |status|
72
+ maybe_continue(status)
73
+ end
74
+ end
75
+ end
76
+ @status
57
77
  rescue Exception => e
58
78
  puts "\n** Failed: #{e.message} FROM\n#{e.backtrace.join("\n")}"
59
79
  raise e
60
80
  end
61
81
  end
62
82
 
63
- def on_read_stdout(data)
64
- @output_text << data
65
- end
66
-
67
- def on_read_stderr(data)
68
- @error_text << data
69
- end
70
-
71
- def on_exit(status)
72
- @last_iteration += 1
73
- @timeout.cancel if @timeout
74
- if @last_iteration < @count
75
- EM.next_tick do
76
- print '+'
77
- STDOUT.flush
78
- do_right_popen(@command, @env)
79
- end
83
+ def maybe_continue(status)
84
+ @iterations += 1
85
+ if @iterations < @count
86
+ do_right_popen(command, env, input) {|status| maybe_continue(status)}
80
87
  else
81
- puts "<" if @count > 1
88
+ @status = status
82
89
  EM.stop
83
90
  end
84
- @status = status
85
91
  end
86
92
  end
93
+ end
87
94
 
95
+ def is_windows?
96
+ return !!(RUBY_PLATFORM =~ /mswin/)
88
97
  end
89
98
 
90
99
  it 'should redirect output' do
91
100
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
92
101
  runner = RightPopenSpec::Runner.new
93
- runner.run_right_popen(command)
94
- runner.status.exitstatus.should == 0
95
- runner.output_text.should == STANDARD_MESSAGE + "\n"
96
- runner.error_text.should == ERROR_MESSAGE + "\n"
102
+ status = runner.run_right_popen(command)
103
+ status.status.exitstatus.should == 0
104
+ status.output_text.should == STANDARD_MESSAGE + "\n"
105
+ status.error_text.should == ERROR_MESSAGE + "\n"
106
+ status.pid.should > 0
97
107
  end
98
108
 
99
109
  it 'should return the right status' do
100
110
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
101
111
  runner = RightPopenSpec::Runner.new
102
- runner.run_right_popen(command)
103
- runner.status.exitstatus.should == EXIT_STATUS
104
- runner.output_text.should == ''
105
- runner.error_text.should == ''
112
+ status = runner.run_right_popen(command)
113
+ status.status.exitstatus.should == EXIT_STATUS
114
+ status.output_text.should == ''
115
+ status.error_text.should == ''
116
+ status.pid.should > 0
117
+ end
118
+
119
+ it 'should correctly handle many small processes' do
120
+ pending 'Set environment variable TEST_STRESS to enable' unless ENV['TEST_STRESS']
121
+ TO_RUN = 100
122
+ command = is_windows? ? "cmd.exe /c exit 0" : "exit 0"
123
+ runner = RightPopenSpec::Runner.new
124
+ @completed = 0
125
+ @started = 0
126
+ run_cmd = Proc.new do
127
+ runner.do_right_popen(command) do |status|
128
+ @completed += 1
129
+ status.status.exitstatus.should == 0
130
+ status.output_text.should == ''
131
+ status.error_text.should == ''
132
+ status.pid.should > 0
133
+ end
134
+ @started += 1
135
+ if @started < TO_RUN
136
+ EM.next_tick { run_cmd.call }
137
+ end
138
+ end
139
+ EM.run do
140
+ EM.next_tick { run_cmd.call }
141
+
142
+ EM::PeriodicTimer.new(1) do
143
+ if @completed >= TO_RUN
144
+ EM.stop
145
+ end
146
+ end
147
+ end
106
148
  end
107
149
 
108
150
  it 'should preserve the integrity of stdout when stderr is unavailable' do
109
151
  count = LARGE_OUTPUT_COUNTER
110
152
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
111
153
  runner = RightPopenSpec::Runner.new
112
- runner.run_right_popen(command)
113
- runner.status.exitstatus.should == 0
154
+ status = runner.run_right_popen(command)
155
+ status.status.exitstatus.should == 0
114
156
 
115
157
  results = ''
116
158
  count.times do |i|
117
159
  results << "stdout #{i}\n"
118
160
  end
119
- runner.output_text.should == results
120
- runner.error_text.should == ''
161
+ status.output_text.should == results
162
+ status.error_text.should == ''
163
+ status.pid.should > 0
121
164
  end
122
165
 
123
166
  it 'should preserve the integrity of stderr when stdout is unavailable' do
124
167
  count = LARGE_OUTPUT_COUNTER
125
168
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stderr_only.rb'))}\" #{count}"
126
169
  runner = RightPopenSpec::Runner.new
127
- runner.run_right_popen(command)
128
- runner.status.exitstatus.should == 0
170
+ status = runner.run_right_popen(command)
171
+ status.status.exitstatus.should == 0
129
172
 
130
173
  results = ''
131
174
  count.times do |i|
132
175
  results << "stderr #{i}\n"
133
176
  end
134
- runner.error_text.should == results
135
- runner.output_text.should == ''
177
+ status.error_text.should == results
178
+ status.output_text.should == ''
179
+ status.pid.should > 0
136
180
  end
137
181
 
138
182
  it 'should preserve the integrity of stdout and stderr despite interleaving' do
139
183
  count = LARGE_OUTPUT_COUNTER
140
184
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_mixed_output.rb'))}\" #{count}"
141
185
  runner = RightPopenSpec::Runner.new
142
- runner.run_right_popen(command)
143
- runner.status.exitstatus.should == 99
186
+ status = runner.run_right_popen(command)
187
+ status.status.exitstatus.should == 99
144
188
 
145
189
  results = ''
146
190
  count.times do |i|
147
191
  results << "stdout #{i}\n"
148
192
  end
149
- runner.output_text.should == results
193
+ status.output_text.should == results
150
194
 
151
195
  results = ''
152
196
  count.times do |i|
153
197
  (results << "stderr #{i}\n") if 0 == i % 10
154
198
  end
155
- runner.error_text.should == results
199
+ status.error_text.should == results
200
+ status.pid.should > 0
156
201
  end
157
-
202
+
158
203
  it 'should setup environment variables' do
159
204
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
160
205
  runner = RightPopenSpec::Runner.new
161
- runner.run_right_popen(command)
162
- runner.status.exitstatus.should == 0
163
- runner.output_text.should_not include('_test_')
164
- runner.run_right_popen(command, :__test__ => '42')
165
- runner.status.exitstatus.should == 0
166
- runner.output_text.should match(/^__test__=42$/)
206
+ status = runner.run_right_popen(command)
207
+ status.status.exitstatus.should == 0
208
+ status.output_text.should_not include('_test_')
209
+ status = runner.run_right_popen(command, :__test__ => '42')
210
+ status.status.exitstatus.should == 0
211
+ status.output_text.should match(/^__test__=42$/)
212
+ status.pid.should > 0
167
213
  end
168
214
 
169
215
  it 'should restore environment variables' do
170
- ENV['__test__'] = '41'
171
- old_envs = {}
172
- ENV.each { |k, v| old_envs[k] = v }
173
- command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
174
- runner = RightPopenSpec::Runner.new
175
- runner.run_right_popen(command, :__test__ => '42')
176
- runner.status.exitstatus.should == 0
177
- runner.output_text.should match(/^__test__=42$/)
178
- ENV.each { |k, v| old_envs[k].should == v }
179
- old_envs.each { |k, v| ENV[k].should == v }
216
+ begin
217
+ ENV['__test__'] = '41'
218
+ old_envs = {}
219
+ ENV.each { |k, v| old_envs[k] = v }
220
+ command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
221
+ runner = RightPopenSpec::Runner.new
222
+ status = runner.run_right_popen(command, :__test__ => '42')
223
+ status.status.exitstatus.should == 0
224
+ status.output_text.should match(/^__test__=42$/)
225
+ ENV.each { |k, v| old_envs[k].should == v }
226
+ old_envs.each { |k, v| ENV[k].should == v }
227
+ status.pid.should > 0
228
+ ensure
229
+ ENV.delete('__test__')
230
+ end
180
231
  end
181
232
 
182
233
  if is_windows?
@@ -185,37 +236,63 @@ describe 'RightScale::popen3' do
185
236
  it 'should merge the PATH variable instead of overriding it' do
186
237
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
187
238
  runner = RightPopenSpec::Runner.new
188
- runner.run_right_popen(command, 'PATH' => "c:/bogus\\bin")
189
- runner.status.exitstatus.should == 0
190
- runner.output_text.should include('PATH=c:\\bogus\\bin;')
239
+ status = runner.run_right_popen(command, 'PATH' => "c:/bogus\\bin")
240
+ status.status.exitstatus.should == 0
241
+ status.output_text.should include('PATH=c:\\bogus\\bin;')
242
+ status.pid.should > 0
191
243
  end
192
244
  else
193
245
  it 'should allow running bash command lines starting with a built-in command' do
194
246
  command = "for i in 1 2 3 4 5; do echo $i;done"
195
247
  runner = RightPopenSpec::Runner.new
196
- runner.run_right_popen(command)
197
- runner.status.exitstatus.should == 0
198
- runner.output_text.should == "1\n2\n3\n4\n5\n"
248
+ status = runner.run_right_popen(command)
249
+ status.status.exitstatus.should == 0
250
+ status.output_text.should == "1\n2\n3\n4\n5\n"
251
+ status.pid.should > 0
199
252
  end
253
+
254
+ it 'should support running background processes' do
255
+ command = "(sleep 20)&"
256
+ now = Time.now
257
+ runner = RightPopenSpec::Runner.new
258
+ status = runner.run_right_popen(command)
259
+ finished = Time.now
260
+ (finished - now).should < 20
261
+ status.did_timeout.should be_false
262
+ status.status.exitstatus.should == 0
263
+ status.output_text.should == ""
264
+ status.pid.should > 0
265
+ end
266
+ end
267
+
268
+ it 'should support raw command arguments' do
269
+ command = is_windows? ? ["cmd.exe", "/c", "echo", "*"] : ["echo", "*"]
270
+ runner = RightPopenSpec::Runner.new
271
+ status = runner.run_right_popen(command)
272
+ status.status.exitstatus.should == 0
273
+ status.output_text.should == "*\n"
274
+ status.pid.should > 0
200
275
  end
201
276
 
202
277
  it 'should run repeatedly without leaking resources' do
203
278
  pending 'Set environment variable TEST_LEAK to enable' unless ENV['TEST_LEAK']
204
279
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
205
280
  runner = RightPopenSpec::Runner.new
206
- runner.run_right_popen(command, nil, nil, REPEAT_TEST_COUNTER)
207
- runner.status.exitstatus.should == 0
208
- runner.output_text.should == STANDARD_MESSAGE + "\n"
209
- runner.error_text.should == ERROR_MESSAGE + "\n"
281
+ status = runner.run_right_popen(command, nil, nil, REPEAT_TEST_COUNTER)
282
+ status.status.exitstatus.should == 0
283
+ status.output_text.should == STANDARD_MESSAGE + "\n"
284
+ status.error_text.should == ERROR_MESSAGE + "\n"
285
+ status.pid.should > 0
210
286
  end
211
287
 
212
288
  it 'should pass input to child process' do
213
289
  command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'increment.rb'))}\""
214
290
  runner = RightPopenSpec::Runner.new
215
- runner.run_right_popen(command, nil, "42\n")
216
- runner.status.exitstatus.should == 0
217
- runner.output_text.should == "43\n"
218
- runner.error_text.should be_empty
291
+ status = runner.run_right_popen(command, nil, "42\n")
292
+ status.status.exitstatus.should == 0
293
+ status.output_text.should == "43\n"
294
+ status.error_text.should be_empty
295
+ status.pid.should > 0
219
296
  end
220
297
 
221
298
  end
data/spec/spec_helper.rb CHANGED
@@ -2,3 +2,19 @@ require 'rubygems'
2
2
  require 'spec'
3
3
  require 'eventmachine'
4
4
  require File.join(File.dirname(__FILE__), '..', 'lib', 'right_popen')
5
+
6
+ RUBY_CMD = 'ruby'
7
+ STANDARD_MESSAGE = 'Standard message'
8
+ ERROR_MESSAGE = 'Error message'
9
+ EXIT_STATUS = 146
10
+
11
+ # manually bump count up for more aggressive multi-processor testing, lessen
12
+ # for a quick smoke test
13
+ LARGE_OUTPUT_COUNTER = 1000
14
+
15
+ # bump up count for most exhaustive leak detection.
16
+ REPEAT_TEST_COUNTER = 256
17
+
18
+ def is_windows?
19
+ return RUBY_PLATFORM =~ /mswin/
20
+ end
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: right_popen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 1.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Messier
8
8
  - Raphael Simon
9
+ - Graham Hughes
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
13
 
13
- date: 2010-06-21 00:00:00 -07:00
14
+ date: 2010-09-08 00:00:00 -07:00
14
15
  default_executable:
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency