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 +7 -0
- data/lib/linux/right_popen.rb +59 -99
- data/lib/right_popen.rb +3 -2
- data/right_popen.gemspec +2 -2
- data/spec/right_popen_spec.rb +182 -105
- data/spec/spec_helper.rb +16 -0
- metadata +3 -2
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
|
data/lib/linux/right_popen.rb
CHANGED
@@ -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
|
-
|
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
|
-
@
|
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(@
|
40
|
+
@target.method(@handler).call(data) if @handler
|
94
41
|
end
|
95
|
-
|
96
|
-
|
97
|
-
def
|
98
|
-
@
|
42
|
+
end
|
43
|
+
module InputHandler
|
44
|
+
def initialize(string)
|
45
|
+
@string = string
|
99
46
|
end
|
100
47
|
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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.
|
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?
|
data/spec/right_popen_spec.rb
CHANGED
@@ -1,182 +1,233 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
2
|
|
3
|
-
|
4
|
-
STANDARD_MESSAGE = 'Standard message'
|
5
|
-
ERROR_MESSAGE = 'Error message'
|
6
|
-
EXIT_STATUS = 146
|
3
|
+
describe 'RightScale::popen3' do
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
REPEAT_TEST_COUNTER = 256
|
24
|
+
attr_accessor :output_text, :error_text, :status, :did_timeout, :pid
|
14
25
|
|
15
|
-
def
|
16
|
-
|
17
|
-
end
|
26
|
+
def on_read_stdout(data)
|
27
|
+
@output_text << data
|
28
|
+
end
|
18
29
|
|
19
|
-
|
30
|
+
def on_read_stderr(data)
|
31
|
+
@error_text << data
|
32
|
+
end
|
20
33
|
|
21
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
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 =>
|
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
|
-
@
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
64
|
-
@
|
65
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
135
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
163
|
-
|
164
|
-
runner.run_right_popen(command, :__test__ => '42')
|
165
|
-
|
166
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
190
|
-
|
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
|
-
|
198
|
-
|
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
|
-
|
208
|
-
|
209
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
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.
|
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-
|
14
|
+
date: 2010-09-08 00:00:00 -07:00
|
14
15
|
default_executable:
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|