Move isolation into config object
Also add Isolation::None for testing.
This commit is contained in:
parent
473aeace90
commit
dec1a39eb5
6 changed files with 167 additions and 24 deletions
|
@ -218,6 +218,7 @@ module Mutant
|
|||
matcher_config: Matcher::Config::DEFAULT,
|
||||
includes: [],
|
||||
requires: [],
|
||||
isolation: Mutant::Isolation::Fork,
|
||||
reporter: Reporter::CLI.new($stdout),
|
||||
zombie: false,
|
||||
expected_coverage: 100.0
|
||||
|
|
|
@ -8,6 +8,7 @@ module Mutant
|
|||
:includes,
|
||||
:requires,
|
||||
:reporter,
|
||||
:isolation,
|
||||
:fail_fast,
|
||||
:zombie,
|
||||
:expected_coverage
|
||||
|
|
|
@ -3,25 +3,49 @@ module Mutant
|
|||
module Isolation
|
||||
Error = Class.new(RuntimeError)
|
||||
|
||||
# Call block in isolation
|
||||
#
|
||||
# This isolation implements the fork strategy.
|
||||
# Future strategies will probably use a process pool that can
|
||||
# handle multiple mutation kills, in-isolation at once.
|
||||
#
|
||||
# @return [Object]
|
||||
#
|
||||
# @raise [Error]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.call(&block)
|
||||
Parallel.map([block], in_processes: 1) do
|
||||
module None
|
||||
|
||||
# Call block in isolation
|
||||
#
|
||||
# @return [Object]
|
||||
#
|
||||
# @raise [Error]
|
||||
# if block terminates abnormal
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.call(&block)
|
||||
block.call
|
||||
end.first
|
||||
rescue Parallel::DeadWorker => exception
|
||||
fail Error, exception
|
||||
rescue => exception
|
||||
fail Error, exception
|
||||
end
|
||||
end
|
||||
|
||||
module Fork
|
||||
|
||||
# Call block in isolation
|
||||
#
|
||||
# This isolation implements the fork strategy.
|
||||
# Future strategies will probably use a process pool that can
|
||||
# handle multiple mutation kills, in-isolation at once.
|
||||
#
|
||||
# @return [Object]
|
||||
# returns block execution result
|
||||
#
|
||||
# @raise [Error]
|
||||
# if block terminates abnormal
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.call(&block)
|
||||
Parallel.map([block], in_processes: 1) do
|
||||
block.call
|
||||
end.first
|
||||
rescue Parallel::DeadWorker => exception
|
||||
fail Error, exception
|
||||
end
|
||||
|
||||
end # Fork
|
||||
|
||||
end # Isolator
|
||||
end # Mutant
|
||||
|
|
|
@ -121,10 +121,10 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run_mutation_test(mutation, test)
|
||||
Isolation.call do
|
||||
config.isolation.call do
|
||||
mutation.insert
|
||||
test.run
|
||||
end.update(test: test, mutation: mutation)
|
||||
end
|
||||
rescue Isolation::Error
|
||||
Result::Test.new(
|
||||
test: test,
|
||||
|
|
|
@ -1,13 +1,36 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Isolation do
|
||||
describe Mutant::Isolation::None do
|
||||
before do
|
||||
@initial = 1
|
||||
end
|
||||
|
||||
describe '.run' do
|
||||
let(:object) { described_class }
|
||||
|
||||
it 'isolates global effects from process' do
|
||||
expect(defined?(::TestConstant)).to be(nil)
|
||||
object.call { ::TestConstant = 1 }
|
||||
expect(defined?(::TestConstant)).to be(nil)
|
||||
it 'does not isolate side effects' do
|
||||
object.call { @initial = 2 }
|
||||
expect(@initial).to be(2)
|
||||
end
|
||||
|
||||
it 'return block value' do
|
||||
expect(object.call { :foo }).to be(:foo)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe Mutant::Isolation::Fork do
|
||||
before do
|
||||
@initial = 1
|
||||
end
|
||||
|
||||
describe '.run' do
|
||||
let(:object) { described_class }
|
||||
|
||||
it 'does isolate side effects' do
|
||||
object.call { @initial = 2 }
|
||||
expect(@initial).to be(1)
|
||||
end
|
||||
|
||||
it 'return block value' do
|
||||
|
|
94
spec/unit/mutant/runner_spec.rb
Normal file
94
spec/unit/mutant/runner_spec.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Runner do
|
||||
let(:object) { described_class.new(env) }
|
||||
|
||||
let(:reporter) { Mutant::Reporter::Trace.new }
|
||||
let(:config) { Mutant::Config::DEFAULT.update(reporter: reporter) }
|
||||
let(:subjects) { [subject_a, subject_b] }
|
||||
|
||||
class Double
|
||||
include Concord.new(:name, :attributes)
|
||||
|
||||
def self.new(name, attributes = {})
|
||||
super
|
||||
end
|
||||
|
||||
def method_missing(name, *arguments)
|
||||
super unless attributes.key?(name)
|
||||
fail "Arguments provided for #{name}" if arguments.any?
|
||||
attributes.fetch(name)
|
||||
end
|
||||
end
|
||||
|
||||
let(:subject_a) { Double.new('Subject A', mutations: mutations_a, tests: subject_a_tests) }
|
||||
let(:subject_b) { Double.new('Subject B', mutations: mutations_b) }
|
||||
|
||||
let(:subject_a_tests) { [test_a1, test_a2] }
|
||||
|
||||
let(:env) do
|
||||
subjects = self.subjects
|
||||
Class.new(Mutant::Env) do
|
||||
define_method(:subjects) { subjects }
|
||||
end.new(config)
|
||||
end
|
||||
|
||||
let(:mutations_a) { [mutation_a1, mutation_a2] }
|
||||
let(:mutations_b) { [] }
|
||||
|
||||
let(:mutation_a1) { Double.new('Mutation A1') }
|
||||
let(:mutation_a2) { Double.new('Mutation A2') }
|
||||
|
||||
let(:test_a1) { Double.new('Test A1') }
|
||||
let(:test_a2) { Double.new('Test A2') }
|
||||
|
||||
let(:test_report_a1) { Double.new('Test Report A1') }
|
||||
|
||||
before do
|
||||
allow(mutation_a1).to receive(:subject).and_return(subject_a)
|
||||
allow(mutation_a1).to receive(:insert)
|
||||
allow(mutation_a2).to receive(:subject).and_return(subject_a)
|
||||
allow(mutation_a2).to receive(:insert)
|
||||
allow(test_a1).to receive(:run).and_return(test_report_a1)
|
||||
allow(mutation_a1).to receive(:killed_by?).with(test_report_a1).and_return(true)
|
||||
allow(mutation_a2).to receive(:killed_by?).with(test_report_a1).and_return(true)
|
||||
end
|
||||
|
||||
before do
|
||||
time = Time.at(0)
|
||||
allow(Time).to receive(:now).and_return(time)
|
||||
end
|
||||
|
||||
describe '#result' do
|
||||
subject { object.result }
|
||||
|
||||
its(:env) { should be(env) }
|
||||
its(:subject_results) { should eql(expected_subject_results) }
|
||||
|
||||
let(:expected_subject_results) do
|
||||
[
|
||||
Mutant::Result::Subject.new(
|
||||
subject: subject_a,
|
||||
mutation_results: [
|
||||
Mutant::Result::Mutation.new(
|
||||
mutation: mutation_a1,
|
||||
runtime: 0.0,
|
||||
test_results: [test_report_a1]
|
||||
),
|
||||
Mutant::Result::Mutation.new(
|
||||
mutation: mutation_a2,
|
||||
runtime: 0.0,
|
||||
test_results: [test_report_a1]
|
||||
)
|
||||
],
|
||||
runtime: 0.0
|
||||
),
|
||||
Mutant::Result::Subject.new(
|
||||
subject: subject_b,
|
||||
mutation_results: [],
|
||||
runtime: 0.0
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue