right_popen 1.0.11 → 1.0.16
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/Rakefile +71 -11
- data/lib/right_popen.rb +5 -5
- data/lib/right_popen/linux/accumulator.rb +117 -0
- data/lib/right_popen/linux/process.rb +148 -0
- data/lib/right_popen/linux/right_popen.rb +170 -0
- data/lib/right_popen/linux/utilities.rb +91 -0
- data/lib/right_popen/version.rb +28 -0
- data/right_popen.gemspec +12 -15
- data/spec/background.rb +28 -0
- data/spec/right_popen/linux/accumulator_spec.rb +272 -0
- data/spec/right_popen/linux/utilities_spec.rb +178 -0
- data/spec/right_popen_spec.rb +185 -253
- data/spec/runner.rb +112 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stdout.rb +28 -0
- metadata +84 -12
- data/lib/linux/right_popen.rb +0 -118
@@ -0,0 +1,178 @@
|
|
1
|
+
#-- -*- mode: ruby; encoding: utf-8 -*-
|
2
|
+
# Copyright: Copyright (c) 2011 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
|
25
|
+
|
26
|
+
module RightScale::RightPopen
|
27
|
+
describe Utilities do
|
28
|
+
describe :run do
|
29
|
+
it 'should collect stdout and stderr' do
|
30
|
+
out, err = Utilities::run("echo foo; echo bar >&2")
|
31
|
+
out.should == "foo\n"
|
32
|
+
err.should == "bar\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should handle utilities that expect input' do
|
36
|
+
out, err = Utilities::run("echo foo; read; echo bar >&2")
|
37
|
+
out.should == "foo\n"
|
38
|
+
err.should == "bar\n"
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should raise an error if the command fails' do
|
42
|
+
lambda { Utilities::run("false") }.should raise_exception(/Command "false" failed with exit status 1/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should raise an error if the command gets killed' do
|
46
|
+
lambda { Utilities::run("kill $$") }.should raise_exception(/Command ".*" failed due to SIGTERM/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should not invoke the shell when inappropriate' do
|
50
|
+
out, err = Utilities::run(["echo", "foo;", "echo", "bar", ">&2"])
|
51
|
+
out.should == "foo; echo bar >&2\n"
|
52
|
+
err.should be_empty
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe :run_collecting_output do
|
57
|
+
it 'should work' do
|
58
|
+
status, out, err = Utilities::run_collecting_output("echo foo; echo bar >&2")
|
59
|
+
status.should be_success
|
60
|
+
out.should == "foo\n"
|
61
|
+
err.should == "bar\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should handle the command failing gracefully' do
|
65
|
+
status, out, err = Utilities::run_collecting_output("false")
|
66
|
+
status.should_not be_success
|
67
|
+
status.exitstatus.should == 1
|
68
|
+
out.should be_empty
|
69
|
+
err.should be_empty
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should handle the command self terminating gracefully' do
|
73
|
+
status, out, err = Utilities::run_collecting_output("kill $$")
|
74
|
+
status.should_not be_success
|
75
|
+
status.exitstatus.should be_nil
|
76
|
+
status.termsig.should == 15
|
77
|
+
out.should be_empty
|
78
|
+
err.should be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should not invoke the shell when inappropriate' do
|
82
|
+
status, out, err = Utilities::run_collecting_output(["echo", "foo;", "read", "foo", ";", "echo", "bar", ">&2"])
|
83
|
+
status.should be_success
|
84
|
+
out.should == "foo; read foo ; echo bar >&2\n"
|
85
|
+
err.should be_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe :run_with_stdin_collecting_output do
|
90
|
+
it 'should work even if subprocess ignores stdin' do
|
91
|
+
status, out, err = Utilities::run_with_stdin_collecting_output("echo foo; echo bar >&2", "foo")
|
92
|
+
status.should be_success
|
93
|
+
out.should == "foo\n"
|
94
|
+
err.should == "bar\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should handle utilities that expect input' do
|
98
|
+
status, out, err = Utilities::run_with_stdin_collecting_output("echo foo; read foo; echo $foo >&2", "blotz")
|
99
|
+
status.should be_success
|
100
|
+
out.should == "foo\n"
|
101
|
+
err.should == "blotz\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should handle the command failing gracefully' do
|
105
|
+
status, out, err = Utilities::run_with_stdin_collecting_output("false", "blotz")
|
106
|
+
status.should_not be_success
|
107
|
+
status.exitstatus.should == 1
|
108
|
+
out.should be_empty
|
109
|
+
err.should be_empty
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should handle the command self terminating gracefully' do
|
113
|
+
status, out, err = Utilities::run_with_stdin_collecting_output("kill $$", "blotz")
|
114
|
+
status.should_not be_success
|
115
|
+
status.exitstatus.should be_nil
|
116
|
+
status.termsig.should == 15
|
117
|
+
out.should be_empty
|
118
|
+
err.should be_empty
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should not invoke the shell when inappropriate' do
|
122
|
+
status, out, err = Utilities::run_with_stdin_collecting_output(["echo", "foo;", "read", "foo", ";", "echo", "bar", ">&2"], "blotz")
|
123
|
+
status.should be_success
|
124
|
+
out.should == "foo; read foo ; echo bar >&2\n"
|
125
|
+
err.should be_empty
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe :run_with_blocks do
|
130
|
+
it 'should work even if subprocess ignores stdin' do
|
131
|
+
status = Utilities::run_with_blocks("echo foo; echo bar >&2",
|
132
|
+
lambda { "foo" },
|
133
|
+
lambda { |b| b.should == "foo\n" },
|
134
|
+
lambda { |b| b.should == "bar\n" })
|
135
|
+
status.should be_success
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should handle interaction' do
|
139
|
+
output_counter = 0
|
140
|
+
seen = {}
|
141
|
+
in_block = lambda {
|
142
|
+
if seen[output_counter]
|
143
|
+
""
|
144
|
+
else
|
145
|
+
seen[output_counter] = true
|
146
|
+
"foo\n"
|
147
|
+
end
|
148
|
+
}
|
149
|
+
status = Utilities::run_with_blocks("for x in 1 2 3; do read var; echo $var; done; echo bar >&2",
|
150
|
+
in_block,
|
151
|
+
lambda { |b| output_counter += 1; b.should == "foo\n" },
|
152
|
+
lambda { |b| b.should == "bar\n" })
|
153
|
+
status.should be_success
|
154
|
+
output_counter.should == 3
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should handle the command failing gracefully' do
|
158
|
+
status = Utilities::run_with_blocks("echo foo; echo bar >&2; false",
|
159
|
+
lambda { "blotz" }, nil, nil)
|
160
|
+
status.should_not be_success
|
161
|
+
status.exitstatus.should == 1
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should handle the command self terminating gracefully' do
|
165
|
+
status = Utilities::run_with_blocks("echo foo; echo bar >&2; kill $$",
|
166
|
+
lambda { "blotz" }, nil, nil)
|
167
|
+
status.should_not be_success
|
168
|
+
status.exitstatus.should be_nil
|
169
|
+
status.termsig.should == 15
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should not invoke the shell when inappropriate' do
|
173
|
+
status, out, err = Utilities::run_with_blocks(["echo", "foo;", "read", "foo", ";", "echo", "bar", ">&2"], nil, lambda {|b| b.should == "foo; read foo ; echo bar >&2\n"}, nil)
|
174
|
+
status.should be_success
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/spec/right_popen_spec.rb
CHANGED
@@ -1,298 +1,230 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'spec_helper')
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'runner'))
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
23
|
-
|
24
|
-
attr_accessor :output_text, :error_text, :status, :did_timeout, :pid
|
4
|
+
module RightScale
|
5
|
+
describe 'popen3' do
|
6
|
+
def is_windows?
|
7
|
+
return !!(RUBY_PLATFORM =~ /mswin/)
|
8
|
+
end
|
25
9
|
|
26
|
-
|
27
|
-
|
28
|
-
|
10
|
+
it 'should redirect output' do
|
11
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
|
12
|
+
runner = Runner.new
|
13
|
+
status = runner.run_right_popen(command)
|
14
|
+
status.status.exitstatus.should == 0
|
15
|
+
status.output_text.should == STANDARD_MESSAGE + "\n"
|
16
|
+
status.error_text.should == ERROR_MESSAGE + "\n"
|
17
|
+
status.pid.should > 0
|
18
|
+
end
|
29
19
|
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
it 'should return the right status' do
|
21
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
|
22
|
+
runner = Runner.new
|
23
|
+
status = runner.run_right_popen(command)
|
24
|
+
status.status.exitstatus.should == EXIT_STATUS
|
25
|
+
status.output_text.should == ''
|
26
|
+
status.error_text.should == ''
|
27
|
+
status.pid.should > 0
|
28
|
+
end
|
33
29
|
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
it 'should correctly handle many small processes' do
|
31
|
+
pending 'Set environment variable TEST_STRESS to enable' unless ENV['TEST_STRESS']
|
32
|
+
TO_RUN = 100
|
33
|
+
command = is_windows? ? "cmd.exe /c exit 0" : "exit 0"
|
34
|
+
runner = Runner.new
|
35
|
+
@completed = 0
|
36
|
+
@started = 0
|
37
|
+
run_cmd = Proc.new do
|
38
|
+
runner.do_right_popen(command) do |status|
|
39
|
+
@completed += 1
|
40
|
+
status.status.exitstatus.should == 0
|
41
|
+
status.output_text.should == ''
|
42
|
+
status.error_text.should == ''
|
43
|
+
status.pid.should > 0
|
37
44
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@status = status
|
42
|
-
@callback.call(self)
|
45
|
+
@started += 1
|
46
|
+
if @started < TO_RUN
|
47
|
+
EM.next_tick { run_cmd.call }
|
43
48
|
end
|
44
49
|
end
|
50
|
+
EM.run do
|
51
|
+
EM.next_tick { run_cmd.call }
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@last_exception = nil
|
50
|
-
@last_iteration = 0
|
51
|
-
end
|
52
|
-
|
53
|
-
def do_right_popen(command, env=nil, input=nil, &callback)
|
54
|
-
status = RunnerStatus.new(command, callback)
|
55
|
-
RightScale.popen3(:command => command,
|
56
|
-
:input => input,
|
57
|
-
:target => status,
|
58
|
-
:environment => env,
|
59
|
-
:stdout_handler => :on_read_stdout,
|
60
|
-
:stderr_handler => :on_read_stderr,
|
61
|
-
:pid_handler => :on_pid,
|
62
|
-
:exit_handler => :on_exit)
|
63
|
-
status
|
64
|
-
end
|
65
|
-
|
66
|
-
def run_right_popen(command, env=nil, input=nil, count=1)
|
67
|
-
begin
|
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
|
53
|
+
EM::PeriodicTimer.new(1) do
|
54
|
+
if @completed >= TO_RUN
|
55
|
+
EM.stop
|
75
56
|
end
|
76
|
-
@status
|
77
|
-
rescue Exception => e
|
78
|
-
puts "\n** Failed: #{e.message} FROM\n#{e.backtrace.join("\n")}"
|
79
|
-
raise e
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def maybe_continue(status)
|
84
|
-
@iterations += 1
|
85
|
-
if @iterations < @count
|
86
|
-
do_right_popen(command, env, input) {|status| maybe_continue(status)}
|
87
|
-
else
|
88
|
-
@status = status
|
89
|
-
EM.stop
|
90
57
|
end
|
91
58
|
end
|
92
59
|
end
|
93
|
-
end
|
94
60
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
runner = RightPopenSpec::Runner.new
|
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
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'should return the right status' do
|
110
|
-
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
|
111
|
-
runner = RightPopenSpec::Runner.new
|
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 }
|
61
|
+
it 'should preserve the integrity of stdout when stderr is unavailable' do
|
62
|
+
count = LARGE_OUTPUT_COUNTER
|
63
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
|
64
|
+
runner = Runner.new
|
65
|
+
status = runner.run_right_popen(command)
|
66
|
+
status.status.exitstatus.should == 0
|
141
67
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
68
|
+
results = ''
|
69
|
+
count.times do |i|
|
70
|
+
results << "stdout #{i}\n"
|
146
71
|
end
|
72
|
+
status.output_text.should == results
|
73
|
+
status.error_text.should == ''
|
74
|
+
status.pid.should > 0
|
147
75
|
end
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'should preserve the integrity of stdout when stderr is unavailable' do
|
151
|
-
count = LARGE_OUTPUT_COUNTER
|
152
|
-
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
|
153
|
-
runner = RightPopenSpec::Runner.new
|
154
|
-
status = runner.run_right_popen(command)
|
155
|
-
status.status.exitstatus.should == 0
|
156
|
-
|
157
|
-
results = ''
|
158
|
-
count.times do |i|
|
159
|
-
results << "stdout #{i}\n"
|
160
|
-
end
|
161
|
-
status.output_text.should == results
|
162
|
-
status.error_text.should == ''
|
163
|
-
status.pid.should > 0
|
164
|
-
end
|
165
76
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
77
|
+
it 'should preserve the integrity of stderr when stdout is unavailable' do
|
78
|
+
count = LARGE_OUTPUT_COUNTER
|
79
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stderr_only.rb'))}\" #{count}"
|
80
|
+
runner = Runner.new
|
81
|
+
status = runner.run_right_popen(command)
|
82
|
+
status.status.exitstatus.should == 0
|
172
83
|
|
173
|
-
|
174
|
-
|
175
|
-
|
84
|
+
results = ''
|
85
|
+
count.times do |i|
|
86
|
+
results << "stderr #{i}\n"
|
87
|
+
end
|
88
|
+
status.error_text.should == results
|
89
|
+
status.output_text.should == ''
|
90
|
+
status.pid.should > 0
|
176
91
|
end
|
177
|
-
status.error_text.should == results
|
178
|
-
status.output_text.should == ''
|
179
|
-
status.pid.should > 0
|
180
|
-
end
|
181
92
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
93
|
+
it 'should preserve the integrity of stdout and stderr despite interleaving' do
|
94
|
+
count = LARGE_OUTPUT_COUNTER
|
95
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_mixed_output.rb'))}\" #{count}"
|
96
|
+
runner = Runner.new
|
97
|
+
status = runner.run_right_popen(command)
|
98
|
+
status.status.exitstatus.should == 99
|
188
99
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
100
|
+
results = ''
|
101
|
+
count.times do |i|
|
102
|
+
results << "stdout #{i}\n"
|
103
|
+
end
|
104
|
+
status.output_text.should == results
|
194
105
|
|
195
|
-
|
196
|
-
|
197
|
-
|
106
|
+
results = ''
|
107
|
+
count.times do |i|
|
108
|
+
(results << "stderr #{i}\n") if 0 == i % 10
|
109
|
+
end
|
110
|
+
status.error_text.should == results
|
111
|
+
status.pid.should > 0
|
198
112
|
end
|
199
|
-
status.error_text.should == results
|
200
|
-
status.pid.should > 0
|
201
|
-
end
|
202
113
|
|
203
|
-
|
204
|
-
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
|
205
|
-
runner = RightPopenSpec::Runner.new
|
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
|
213
|
-
end
|
214
|
-
|
215
|
-
it 'should restore environment variables' do
|
216
|
-
begin
|
217
|
-
ENV['__test__'] = '41'
|
218
|
-
old_envs = {}
|
219
|
-
ENV.each { |k, v| old_envs[k] = v }
|
114
|
+
it 'should setup environment variables' do
|
220
115
|
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
|
221
|
-
runner =
|
116
|
+
runner = Runner.new
|
117
|
+
status = runner.run_right_popen(command)
|
118
|
+
status.status.exitstatus.should == 0
|
119
|
+
status.output_text.should_not include('_test_')
|
222
120
|
status = runner.run_right_popen(command, :__test__ => '42')
|
223
121
|
status.status.exitstatus.should == 0
|
224
122
|
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
123
|
status.pid.should > 0
|
228
|
-
ensure
|
229
|
-
ENV.delete('__test__')
|
230
124
|
end
|
231
|
-
end
|
232
125
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
126
|
+
it 'should restore environment variables' do
|
127
|
+
begin
|
128
|
+
ENV['__test__'] = '41'
|
129
|
+
old_envs = {}
|
130
|
+
ENV.each { |k, v| old_envs[k] = v }
|
131
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
|
132
|
+
runner = Runner.new
|
133
|
+
status = runner.run_right_popen(command, :__test__ => '42')
|
134
|
+
status.status.exitstatus.should == 0
|
135
|
+
status.output_text.should match(/^__test__=42$/)
|
136
|
+
ENV.each { |k, v| old_envs[k].should == v }
|
137
|
+
old_envs.each { |k, v| ENV[k].should == v }
|
138
|
+
status.pid.should > 0
|
139
|
+
ensure
|
140
|
+
ENV.delete('__test__')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
if is_windows?
|
145
|
+
# FIX: this behavior is currently specific to Windows but should probably be
|
146
|
+
# implemented for Linux.
|
147
|
+
it 'should merge the PATH variable instead of overriding it' do
|
148
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'print_env.rb'))}\""
|
149
|
+
runner = Runner.new
|
150
|
+
status = runner.run_right_popen(command, 'PATH' => "c:/bogus\\bin")
|
151
|
+
status.status.exitstatus.should == 0
|
152
|
+
status.output_text.should include('c:\\bogus\\bin;')
|
153
|
+
status.pid.should > 0
|
154
|
+
end
|
155
|
+
else
|
156
|
+
it 'should allow running bash command lines starting with a built-in command' do
|
157
|
+
command = "for i in 1 2 3 4 5; do echo $i;done"
|
158
|
+
runner = Runner.new
|
159
|
+
status = runner.run_right_popen(command)
|
160
|
+
status.status.exitstatus.should == 0
|
161
|
+
status.output_text.should == "1\n2\n3\n4\n5\n"
|
162
|
+
status.pid.should > 0
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should support running background processes' do
|
166
|
+
command = "(sleep 20)&"
|
167
|
+
now = Time.now
|
168
|
+
runner = Runner.new
|
169
|
+
status = runner.run_right_popen(command)
|
170
|
+
finished = Time.now
|
171
|
+
(finished - now).should < 20
|
172
|
+
status.did_timeout.should be_false
|
173
|
+
status.status.exitstatus.should == 0
|
174
|
+
status.output_text.should == ""
|
175
|
+
status.pid.should > 0
|
176
|
+
end
|
243
177
|
end
|
244
|
-
|
245
|
-
it 'should
|
246
|
-
command =
|
247
|
-
runner =
|
178
|
+
|
179
|
+
it 'should support raw command arguments' do
|
180
|
+
command = is_windows? ? ["cmd.exe", "/c", "echo", "*"] : ["echo", "*"]
|
181
|
+
runner = Runner.new
|
248
182
|
status = runner.run_right_popen(command)
|
249
183
|
status.status.exitstatus.should == 0
|
250
|
-
status.output_text.should == "
|
184
|
+
status.output_text.should == "*\n"
|
251
185
|
status.pid.should > 0
|
252
186
|
end
|
253
187
|
|
254
|
-
it 'should
|
255
|
-
|
256
|
-
|
257
|
-
runner =
|
258
|
-
status = runner.run_right_popen(command)
|
259
|
-
finished = Time.now
|
260
|
-
(finished - now).should < 20
|
261
|
-
status.did_timeout.should be_false
|
188
|
+
it 'should run repeatedly without leaking resources' do
|
189
|
+
pending 'Set environment variable TEST_LEAK to enable' unless ENV['TEST_LEAK']
|
190
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
|
191
|
+
runner = Runner.new
|
192
|
+
status = runner.run_right_popen(command, nil, nil, REPEAT_TEST_COUNTER)
|
262
193
|
status.status.exitstatus.should == 0
|
263
|
-
status.output_text.should == ""
|
194
|
+
status.output_text.should == STANDARD_MESSAGE + "\n"
|
195
|
+
status.error_text.should == ERROR_MESSAGE + "\n"
|
264
196
|
status.pid.should > 0
|
265
197
|
end
|
266
|
-
end
|
267
198
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
199
|
+
it 'should pass input to child process' do
|
200
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'increment.rb'))}\""
|
201
|
+
runner = Runner.new
|
202
|
+
status = runner.run_right_popen(command, nil, "42\n")
|
203
|
+
status.status.exitstatus.should == 0
|
204
|
+
status.output_text.should == "43\n"
|
205
|
+
status.error_text.should be_empty
|
206
|
+
status.pid.should > 0
|
207
|
+
end
|
276
208
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
end
|
209
|
+
it 'should handle child processes that close stdout but keep running' do
|
210
|
+
pending 'not implemented for windows' if is_windows?
|
211
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'stdout.rb'))}\""
|
212
|
+
runner = Runner.new
|
213
|
+
status = runner.run_right_popen(command, nil, nil)
|
214
|
+
status.did_timeout.should be_true
|
215
|
+
status.output_text.should be_empty
|
216
|
+
status.error_text.should == "Closing stdout\n"
|
217
|
+
end
|
287
218
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
219
|
+
it 'should handle child processes that spawn long running background processes' do
|
220
|
+
pending 'not implemented for windows' if is_windows?
|
221
|
+
command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'background.rb'))}\""
|
222
|
+
runner = Runner.new
|
223
|
+
status = runner.run_right_popen(command, nil, nil)
|
224
|
+
status.status.exitstatus.should == 0
|
225
|
+
status.did_timeout.should be_false
|
226
|
+
status.output_text.should be_empty
|
227
|
+
status.error_text.should be_empty
|
228
|
+
end
|
296
229
|
end
|
297
|
-
|
298
230
|
end
|