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:
Dan Kubb 2015-07-23 13:45:43 -07:00
parent d4426ce93a
commit 63c586f6bb
2 changed files with 43 additions and 39 deletions

View file

@ -38,10 +38,10 @@ module Mutant
#
# @api private
def self.call(&block)
reader, writer = IO.pipe.map(&:binmode)
IO.pipe(binmode: true) do |reader, writer|
begin
pid = Process.fork do
File.open('/dev/null', 'w') do |file|
File.open(File::NULL, 'w') do |file|
$stderr.reopen(file)
reader.close
writer.write(Marshal.dump(block.call))
@ -51,11 +51,13 @@ module Mutant
writer.close
Marshal.load(reader.read)
rescue => exception
raise Error, exception
ensure
Process.waitpid(pid) if pid
end
end
rescue => exception
raise Error, exception
end
end # Fork

View file

@ -64,11 +64,17 @@ RSpec.describe Mutant::Isolation::Fork do
end
end
it 'uses primitives in correct order when fork succeeds' do
reader, writer = double('reader'), double('writer')
expect(IO).to receive(:pipe).ordered.and_return([reader, writer])
expect(reader).to receive(:binmode).and_return(reader).ordered
expect(writer).to receive(:binmode).and_return(writer).ordered
context 'uses primitives in correct order' do
let(:reader) { double('reader') }
let(:writer) { double('writer') }
before do
expect(IO).to receive(:pipe).with(binmode: true).ordered do |&block|
block.call([reader, writer])
end
end
it 'when fork succeeds' do
pid = double('PID')
expect(Process).to receive(:fork).ordered.and_yield.and_return(pid)
file = double('file')
@ -84,17 +90,13 @@ RSpec.describe Mutant::Isolation::Fork do
expect(object.call { :foo }).to be(:foo)
end
it 'uses primitives in correct order when fork fails' do
reader, writer = double('reader'), double('writer')
expect(IO).to receive(:pipe).ordered.and_return([reader, writer])
expect(reader).to receive(:binmode).and_return(reader).ordered
expect(writer).to receive(:binmode).and_return(writer).ordered
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