Change Mutant::Isolation.call to use block form of IO.pipe
* The block form automatically closes the reader and writer if they have not already been closed. This prevents resource leakage if the method exits due to an exception. * Refactor specs to have common setup performed in a before block.
This commit is contained in:
parent
d4426ce93a
commit
63c586f6bb
2 changed files with 43 additions and 39 deletions
|
@ -38,23 +38,25 @@ module Mutant
|
||||||
#
|
#
|
||||||
# @api private
|
# @api private
|
||||||
def self.call(&block)
|
def self.call(&block)
|
||||||
reader, writer = IO.pipe.map(&:binmode)
|
IO.pipe(binmode: true) do |reader, writer|
|
||||||
|
begin
|
||||||
|
pid = Process.fork do
|
||||||
|
File.open(File::NULL, 'w') do |file|
|
||||||
|
$stderr.reopen(file)
|
||||||
|
reader.close
|
||||||
|
writer.write(Marshal.dump(block.call))
|
||||||
|
writer.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
pid = Process.fork do
|
|
||||||
File.open('/dev/null', 'w') do |file|
|
|
||||||
$stderr.reopen(file)
|
|
||||||
reader.close
|
|
||||||
writer.write(Marshal.dump(block.call))
|
|
||||||
writer.close
|
writer.close
|
||||||
|
Marshal.load(reader.read)
|
||||||
|
ensure
|
||||||
|
Process.waitpid(pid) if pid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
writer.close
|
|
||||||
Marshal.load(reader.read)
|
|
||||||
rescue => exception
|
rescue => exception
|
||||||
raise Error, exception
|
raise Error, exception
|
||||||
ensure
|
|
||||||
Process.waitpid(pid) if pid
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end # Fork
|
end # Fork
|
||||||
|
|
|
@ -64,37 +64,39 @@ RSpec.describe Mutant::Isolation::Fork do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'uses primitives in correct order when fork succeeds' do
|
context 'uses primitives in correct order' do
|
||||||
reader, writer = double('reader'), double('writer')
|
let(:reader) { double('reader') }
|
||||||
expect(IO).to receive(:pipe).ordered.and_return([reader, writer])
|
let(:writer) { double('writer') }
|
||||||
expect(reader).to receive(:binmode).and_return(reader).ordered
|
|
||||||
expect(writer).to receive(:binmode).and_return(writer).ordered
|
|
||||||
pid = double('PID')
|
|
||||||
expect(Process).to receive(:fork).ordered.and_yield.and_return(pid)
|
|
||||||
file = double('file')
|
|
||||||
expect(File).to receive(:open).ordered.with('/dev/null', 'w').and_yield(file)
|
|
||||||
expect($stderr).to receive(:reopen).ordered.with(file)
|
|
||||||
expect(reader).to receive(:close).ordered
|
|
||||||
expect(writer).to receive(:write).ordered.with(Marshal.dump(:foo))
|
|
||||||
expect(writer).to receive(:close).ordered
|
|
||||||
expect(writer).to receive(:close).ordered
|
|
||||||
expect(reader).to receive(:read).ordered.and_return(Marshal.dump(:foo))
|
|
||||||
expect(Process).to receive(:waitpid).with(pid)
|
|
||||||
|
|
||||||
expect(object.call { :foo }).to be(:foo)
|
before do
|
||||||
end
|
expect(IO).to receive(:pipe).with(binmode: true).ordered do |&block|
|
||||||
|
block.call([reader, writer])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'uses primitives in correct order when fork fails' do
|
it 'when fork succeeds' do
|
||||||
reader, writer = double('reader'), double('writer')
|
pid = double('PID')
|
||||||
expect(IO).to receive(:pipe).ordered.and_return([reader, writer])
|
expect(Process).to receive(:fork).ordered.and_yield.and_return(pid)
|
||||||
expect(reader).to receive(:binmode).and_return(reader).ordered
|
file = double('file')
|
||||||
expect(writer).to receive(:binmode).and_return(writer).ordered
|
expect(File).to receive(:open).ordered.with('/dev/null', 'w').and_yield(file)
|
||||||
expect(Process).to receive(:fork).ordered.and_return(nil)
|
expect($stderr).to receive(:reopen).ordered.with(file)
|
||||||
expect(Process).to_not receive(:waitpid)
|
expect(reader).to receive(:close).ordered
|
||||||
|
expect(writer).to receive(:write).ordered.with(Marshal.dump(:foo))
|
||||||
|
expect(writer).to receive(:close).ordered
|
||||||
|
expect(writer).to receive(:close).ordered
|
||||||
|
expect(reader).to receive(:read).ordered.and_return(Marshal.dump(:foo))
|
||||||
|
expect(Process).to receive(:waitpid).with(pid)
|
||||||
|
|
||||||
expect(writer).to receive(:close).ordered
|
expect(object.call { :foo }).to be(:foo)
|
||||||
expect(reader).to receive(:read).ordered.and_return(Marshal.dump(:foo))
|
end
|
||||||
expect(object.call).to be(:foo)
|
|
||||||
|
it 'when fork fails' do
|
||||||
|
expect(Process).to receive(:fork).ordered.and_return(nil)
|
||||||
|
expect(Process).to_not receive(:waitpid)
|
||||||
|
expect(writer).to receive(:close).ordered
|
||||||
|
expect(reader).to receive(:read).ordered.and_return(Marshal.dump(:foo))
|
||||||
|
expect(object.call).to be(:foo)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue