right_popen 1.1.3 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,127 +0,0 @@
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
- module RightScale
25
- module RightPopen
26
-
27
- # @deprecated this seems like test harness code smell, not production code
28
- class Accumulator
29
- READ_CHUNK_SIZE = 4096
30
-
31
- def initialize(process, inputs, read_callbacks, outputs, write_callbacks)
32
- warn 'WARNING: RightScale::RightPopen::Accumulator is deprecated and will be removed.'
33
- @process = process
34
- @inputs = inputs
35
- @outputs = outputs
36
- null = Proc.new {}
37
- @reads = {}
38
- @writes = {}
39
- inputs.zip(read_callbacks).each do |pair|
40
- input, callback = pair
41
- @reads[input] = callback
42
- end
43
- outputs.zip(write_callbacks).each do |pair|
44
- output, callback = pair
45
- @writes[output] = callback
46
- end
47
- @writebuffers = {}
48
- @status = nil
49
- end
50
-
51
- def status
52
- unless @status
53
- @status = ::Process.waitpid2(@process.pid, ::Process::WNOHANG)
54
- end
55
- @status
56
- end
57
-
58
- def tick(sleep_time = 0.1)
59
- return true unless @status.nil?
60
-
61
- status
62
-
63
- inputs = @inputs.dup
64
- outputs = @outputs.dup
65
- ready = nil
66
- while ready.nil?
67
- begin
68
- # in theory, we should note "exceptional conditions" and
69
- # permit procs for those, too. In practice there are only
70
- # two times when exceptional conditions occur: out of band
71
- # data in TCP connections and "packet mode" for
72
- # pseudoterminals. We care about neither of these,
73
- # therefore ignore exceptional conditions.
74
- ready = IO.select(inputs, outputs, nil, sleep_time)
75
- rescue Errno::EAGAIN, Errno::EINTR
76
- end
77
- end unless inputs.empty? && outputs.empty?
78
-
79
- ready[0].each do |fdes|
80
- if fdes.eof?
81
- fdes.close
82
- @inputs.delete(fdes)
83
- else
84
- chunk = fdes.readpartial(READ_CHUNK_SIZE)
85
- @reads[fdes].call(chunk) if @reads[fdes]
86
- end
87
- end unless ready.nil? || ready[0].nil?
88
- ready[1].each do |fdes|
89
- buffered = @writebuffers[fdes]
90
- buffered = @writes[fdes].call if @writes[fdes] if buffered.nil? || buffered.empty?
91
- if buffered.nil?
92
- fdes.close
93
- @outputs.delete(fdes)
94
- elsif !buffered.empty?
95
- begin
96
- amount = fdes.write_nonblock buffered
97
- @writebuffers[fdes] = buffered[amount..-1]
98
- rescue Errno::EPIPE
99
- # subprocess closed the pipe; fine.
100
- fdes.close
101
- @outputs.delete(fdes)
102
- end
103
- end
104
- end unless ready.nil? || ready[1].nil?
105
-
106
- return !@status.nil?
107
- end
108
-
109
- def number_waiting_on
110
- @inputs.size + @outputs.size
111
- end
112
-
113
- def cleanup
114
- @inputs.each {|p| p.close unless p.closed? }
115
- @outputs.each {|p| p.close unless p.closed? }
116
- @status = ::Process.waitpid2(@process.pid) if @status.nil?
117
- end
118
-
119
- def run_to_completion(sleep_time=0.1)
120
- until tick(sleep_time)
121
- break if number_waiting_on == 0
122
- end
123
- cleanup
124
- end
125
- end
126
- end
127
- end
@@ -1,94 +0,0 @@
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__), "process"))
25
- require File.expand_path(File.join(File.dirname(__FILE__), "accumulator"))
26
-
27
- module RightScale
28
- module RightPopen
29
-
30
- # @deprecated this seems like test harness code smell, not production code
31
- module Utilities
32
- module_function
33
-
34
- SIGNAL_LOOKUP = Signal.list.invert
35
-
36
- def reason(status)
37
- if status.exitstatus
38
- "with exit status #{status.exitstatus}"
39
- else
40
- "due to SIG#{SIGNAL_LOOKUP[status.termsig]}"
41
- end
42
- end
43
- private :reason
44
-
45
- def run(cmd, parameters={})
46
- status, out, err = run_collecting_output(cmd, parameters)
47
- unless status.success?
48
- raise "Command \"#{cmd}\" failed #{reason(status)}: " +
49
- "stdout #{out}, stderr #{err}"
50
- end
51
- [out, err]
52
- end
53
-
54
- def run_with_stdin_collecting_output(cmd, input, parameters={})
55
- out = StringIO.new
56
- err = StringIO.new
57
- first = true
58
- status = run_with_blocks(cmd,
59
- Proc.new {
60
- if (first)
61
- first = false
62
- input
63
- else
64
- nil
65
- end},
66
- Proc.new {|s| out.write(s)},
67
- Proc.new {|s| err.write(s)})
68
- [status, out.string, err.string]
69
- end
70
- alias_method :run_input, :run_with_stdin_collecting_output
71
-
72
- def run_collecting_output(cmd, parameters={})
73
- out = StringIO.new
74
- err = StringIO.new
75
- status = run_with_blocks(cmd, nil, Proc.new {|s| out.write(s)},
76
- Proc.new {|s| err.write(s)})
77
- [status, out.string, err.string]
78
- end
79
- alias_method :spawn, :run_collecting_output
80
-
81
- def run_with_blocks(cmd, stdin_block, stdout_block, stderr_block, parameters={})
82
- warn 'WARNING: RightScale::RightPopen::Utilities are deprecated and will be removed.'
83
- process = Process.new(parameters)
84
- process.spawn(cmd, ::RightScale::RightPopen::TargetProxy.new(parameters))
85
- process.wait_for_exec
86
- a = Accumulator.new(process,
87
- [process.stdout, process.stderr], [stdout_block, stderr_block],
88
- [process.stdin], [stdin_block])
89
- a.run_to_completion
90
- a.status[1]
91
- end
92
- end
93
- end
94
- end
@@ -1,28 +0,0 @@
1
- #-- -*- mode: ruby; encoding: utf-8 -*-
2
- # Copyright: Copyright (c) 2011-2013 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
- module RightScale
25
- module RightPopen
26
- VERSION = "1.1.3"
27
- end
28
- end
@@ -1,28 +0,0 @@
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
- fork {
25
- sleep 30
26
- puts "Done!"
27
- }
28
- exit 0
@@ -1,2 +0,0 @@
1
- input = gets.to_i
2
- $stdout.puts input + 1
@@ -1,2 +0,0 @@
1
- puts "__test__=#{ENV['__test__']}" if ENV['__test__']
2
- puts "PATH=#{ENV['PATH']}"
@@ -1,12 +0,0 @@
1
- count = ARGV[0] ? ARGV[0].to_i : 1
2
- exit_code = ARGV[1] ? ARGV[1].to_i : 0
3
-
4
- STDOUT.sync=true
5
- STDERR.sync=true
6
-
7
- count.times do |i|
8
- $stderr.puts "stderr #{i}" if 0 == i % 10
9
- $stdout.puts "stdout #{i}"
10
- end
11
-
12
- exit exit_code
@@ -1,2 +0,0 @@
1
- $stdout.puts ARGV[0]
2
- $stderr.puts ARGV[1]
@@ -1 +0,0 @@
1
- exit ARGV[0].to_i
@@ -1,5 +0,0 @@
1
- count = ARGV[0] ? ARGV[0].to_i : 1
2
-
3
- count.times do |i|
4
- $stderr.puts "stderr #{i}"
5
- end
@@ -1,5 +0,0 @@
1
- count = ARGV[0] ? ARGV[0].to_i : 1
2
-
3
- count.times do |i|
4
- $stdout.puts "stdout #{i}"
5
- end
@@ -1,267 +0,0 @@
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 Accumulator do
28
- before(:each) do
29
- @process = flexmock("process")
30
- @process.should_receive(:pid).and_return(42)
31
- end
32
-
33
- describe "#tick" do
34
- context 'with a live child' do
35
- before(:each) do
36
- @input = flexmock("input")
37
- @output = flexmock("output")
38
- @read = flexmock("read")
39
- @write = flexmock("write")
40
- end
41
-
42
- it 'should skip calling select if no pipes are given' do
43
- a = Accumulator.new(@process, [], [], [], [])
44
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
45
- a.tick.should be_false
46
- end
47
-
48
- it 'should just check waitpid if the select times out' do
49
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
50
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).once.and_return([[], [], []])
51
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
52
- a.tick.should be_false
53
- end
54
-
55
- it 'should use the timeout value in the select' do
56
- value = flexmock("value")
57
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
58
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, value).once.and_return([[], [], []])
59
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
60
- a.tick(value).should be_false
61
- end
62
-
63
- it 'should retry the select when seeing Errno::EAGAIN or Errno::EINTR' do
64
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
65
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).times(3).and_raise(Errno::EAGAIN).and_raise(Errno::EINTR).and_return([[], [], []])
66
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
67
- a.tick.should be_false
68
- end
69
-
70
- it 'should read data from the pipe and call the reader if it is ready' do
71
- value = flexmock("value")
72
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
73
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).once.and_return([[@input], [], []])
74
- @input.should_receive(:eof?).once.and_return(false)
75
- @input.should_receive(:readpartial).with(Accumulator::READ_CHUNK_SIZE).once.and_return(value)
76
- @read.should_receive(:call).with(value).once
77
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
78
- a.tick.should be_false
79
- end
80
-
81
- it 'should read data from the pipe and throw it away if no reader' do
82
- value = flexmock("value")
83
- a = Accumulator.new(@process, [@input], [], [@output], [@write])
84
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).once.and_return([[@input], [], []])
85
- @input.should_receive(:eof?).once.and_return(false)
86
- @input.should_receive(:readpartial).with(Accumulator::READ_CHUNK_SIZE).once.and_return(value)
87
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
88
- a.tick.should be_false
89
- end
90
-
91
- it 'should call the writer and then write data to the pipe if it is ready' do
92
- value = flexmock("value")
93
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
94
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).once.and_return([[], [@output], []])
95
- @write.should_receive(:call).with().once.and_return(value)
96
- value.should_receive(:[]).with(30..-1).and_return("")
97
- value.should_receive("empty?").and_return(false)
98
- @output.should_receive(:write_nonblock).with(value).once.and_return(30)
99
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
100
- a.tick.should be_false
101
- end
102
-
103
- it 'should only call the writer when it is stalling' do
104
- value = flexmock("value")
105
- other = flexmock("other value")
106
- a = Accumulator.new(@process, [@input], [@read], [@output], [@write])
107
- flexmock(::IO).should_receive(:select).with([@input], [@output], nil, 0.1).and_return([[], [@output], []])
108
- @write.should_receive(:call).with().once.and_return(value)
109
- value.should_receive(:[]).with(30..-1).and_return(other)
110
- other.should_receive(:[]).with(20..-1).and_return("")
111
- value.should_receive("empty?").and_return(false)
112
- other.should_receive("empty?").and_return(false)
113
- @output.should_receive(:write_nonblock).with(value).once.and_return(30)
114
- @output.should_receive(:write_nonblock).with(other).once.and_return(20)
115
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).and_return(nil)
116
- a.tick.should be_false
117
- a.tick.should be_false
118
- end
119
-
120
- it 'should not read data from the pipe any more if EOF has been reached' do
121
- value = flexmock("value")
122
- a = Accumulator.new(@process, [@input], [@read], [], [])
123
- flexmock(::IO).should_receive(:select).with([@input], [], nil, 0.1).once.and_return([[@input], [], []])
124
- @input.should_receive(:eof?).once.and_return(true)
125
- @input.should_receive(:close).once
126
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).twice.and_return(nil)
127
- a.tick.should be_false
128
- a.tick.should be_false
129
- end
130
-
131
- it 'should not write data to the pipe any more if the caller has no more data' do
132
- value = flexmock("value")
133
- a = Accumulator.new(@process, [], [], [@output], [@write])
134
- flexmock(::IO).should_receive(:select).with([], [@output], nil, 0.1).once.and_return([[], [@output], []])
135
- @write.should_receive(:call).once.and_return(nil)
136
- @output.should_receive(:close).once
137
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).twice.and_return(nil)
138
- a.tick.should be_false
139
- a.tick.should be_false
140
- end
141
-
142
- it 'should not write data to the pipe any more if the caller is nil' do
143
- a = Accumulator.new(@process, [], [], [@output], [nil])
144
- flexmock(::IO).should_receive(:select).with([], [@output], nil, 0.1).once.and_return([[], [@output], []])
145
- @output.should_receive(:close).once
146
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).twice.and_return(nil)
147
- a.tick.should be_false
148
- a.tick.should be_false
149
- end
150
- end
151
-
152
- it 'should update the status if waitpid is successful' do
153
- a = Accumulator.new(@process, [], [], [], [])
154
- status = flexmock("status")
155
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(status)
156
- a.tick.should be_true
157
- end
158
-
159
- it 'should return true if the process has already been waited on' do
160
- a = Accumulator.new(@process, [], [], [], [])
161
- status = flexmock("status")
162
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(status)
163
- a.tick.should be_true
164
- a.tick.should be_true
165
- end
166
- end
167
-
168
- describe "#number_waiting_on" do
169
- it 'should return 0 when no pipes are left' do
170
- a = Accumulator.new(@process, [], [], [], [])
171
- a.number_waiting_on.should == 0
172
- end
173
-
174
- it 'should return 1 when one pipe is left' do
175
- pipe = flexmock("pipe")
176
- a = Accumulator.new(@process, [pipe], [nil], [], [])
177
- a.number_waiting_on.should == 1
178
- end
179
-
180
- it 'should return add readers and writers' do
181
- pipe = flexmock("pipe")
182
- a = Accumulator.new(@process, [pipe, pipe, pipe], [], [pipe], [])
183
- a.number_waiting_on.should == 4
184
- end
185
-
186
- it 'should transition from 0 to 1 as pipes are removed' do
187
- pipe = flexmock("pipe")
188
- read = flexmock("read")
189
- a = Accumulator.new(@process, [pipe], [read], [], [])
190
- flexmock(::IO).should_receive(:select).with([pipe], [], nil, 0.1).once.and_return([[pipe], [], []])
191
- pipe.should_receive(:eof?).and_return(true)
192
- pipe.should_receive(:close)
193
- flexmock(::Process).should_receive(:waitpid2).with(42, ::Process::WNOHANG).once.and_return(nil)
194
- a.number_waiting_on.should == 1
195
- a.tick.should be_false
196
- a.number_waiting_on.should == 0
197
- end
198
- end
199
-
200
- describe "#cleanup" do
201
- it 'should do nothing if no pipes are left open and the process is reaped' do
202
- pending 'needs refactoring if actually in use'
203
- a = Accumulator.new(@process, [], [], [], [])
204
- flexmock(::Process).should_receive(:waitpid2).never
205
- a.cleanup
206
- end
207
-
208
- it 'should just call waitpid if no pipes are left open' do
209
- value = flexmock("value")
210
- a = Accumulator.new(@process, [], [], [], [])
211
- flexmock(::Process).should_receive(:waitpid2).with(42).once.and_return(value)
212
- a.cleanup
213
- end
214
-
215
- it 'should close all open pipes' do
216
- pending 'needs refactoring if actually in use'
217
- a, b, c = flexmock("a"), flexmock("b"), flexmock("c")
218
- acc = Accumulator.new(@process, [a, b], [], [c], [])
219
- [a, b].each {|fdes| fdes.should_receive(:close).with().once }
220
- [a, b].each {|fdes| fdes.should_receive(:closed?).and_return(false) }
221
- [c].each {|fdes| fdes.should_receive(:closed?).and_return(true) }
222
- flexmock(::Process).should_receive(:waitpid2).never
223
- acc.cleanup
224
- end
225
-
226
- it 'should close all open pipes and reap zombies if needed' do
227
- value = flexmock("value")
228
- a, b, c = flexmock("a"), flexmock("b"), flexmock("c")
229
- acc = Accumulator.new(@process, [b, c], [], [a], [])
230
- [a, b].each {|fdes| fdes.should_receive(:close).with().once }
231
- [a, b].each {|fdes| fdes.should_receive(:closed?).and_return(false) }
232
- [c].each {|fdes| fdes.should_receive(:closed?).and_return(true) }
233
- flexmock(::Process).should_receive(:waitpid2).with(42).once.and_return(value)
234
- acc.cleanup
235
- end
236
- end
237
-
238
- describe "#run_to_completion" do
239
- it 'should run ticks until it is true' do
240
- value = flexmock("value")
241
- acc = flexmock(Accumulator.new(@process, [], [], [], []))
242
- acc.should_receive(:tick).with(value).times(3).and_return(false).and_return(false).and_return(true)
243
- acc.should_receive(:cleanup).once
244
- acc.should_receive(:number_waiting_on).and_return(1)
245
- acc.run_to_completion(value)
246
- end
247
-
248
- it 'should abort the loop early if there are no remaining pipes' do
249
- value = flexmock("value")
250
- acc = flexmock(Accumulator.new(@process, [], [], [], []))
251
- acc.should_receive(:tick).with(value).twice.and_return(false)
252
- acc.should_receive(:cleanup).once
253
- acc.should_receive(:number_waiting_on).and_return(1).and_return(0)
254
- acc.run_to_completion(value)
255
- end
256
-
257
- it 'should abort the loop after one iteration if there never were any pipes' do
258
- value = flexmock("value")
259
- acc = flexmock(Accumulator.new(@process, [], [], [], []))
260
- acc.should_receive(:tick).with(value).once.and_return(false)
261
- acc.should_receive(:cleanup).once
262
- acc.should_receive(:number_waiting_on).and_return(0)
263
- acc.run_to_completion(value)
264
- end
265
- end
266
- end
267
- end