right_popen 1.1.3 → 3.0.1

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.
@@ -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