right_popen 1.0.9 → 1.0.11

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