free_mutant/spec/unit/mutant/env/bootstrap_spec.rb
2016-03-19 21:25:51 -07:00

188 lines
4.9 KiB
Ruby

# This spec is a good example for:
#
# If test look that ugly the class under test sucks.
#
# As the bootstrap needs to infect global VM state
# this is to some degree acceptable.
#
# Still the bootstrap needs to be cleaned up.
# And the change that added this warning did the groundwork.
RSpec.describe Mutant::Env::Bootstrap do
let(:matcher_config) { Mutant::Matcher::Config::DEFAULT }
let(:integration) { instance_double(Mutant::Integration) }
let(:integration_class) { instance_double(Class) }
let(:object_space_modules) { [] }
let(:config) do
Mutant::Config::DEFAULT.with(
includes: [],
integration: integration_class,
jobs: 1,
matcher: matcher_config,
reporter: instance_double(Mutant::Reporter),
requires: []
)
end
let(:expected_env) do
Mutant::Env.new(
actor_env: Mutant::Actor::Env.new(Thread),
config: config,
integration: integration,
matchable_scopes: [],
mutations: [],
parser: Mutant::Parser.new,
selector: Mutant::Selector::Expression.new(integration),
subjects: []
)
end
shared_examples_for 'bootstrap call' do
it { should eql(expected_env) }
end
def expect_warning
expect(config.reporter).to receive(:warn)
.with(expected_warning)
.and_return(config.reporter)
end
before do
expect(integration_class).to receive(:new)
.with(config)
.and_return(integration)
expect(integration).to receive_messages(setup: integration)
expect(ObjectSpace).to receive(:each_object)
.with(Module)
.and_return(object_space_modules.each)
end
describe '#warn' do
let(:object) { described_class.new(config) }
let(:expected_warning) { instance_double(String) }
subject { object.warn(expected_warning) }
before { expect_warning }
it_behaves_like 'a command method'
end
describe '.call' do
subject { described_class.call(config) }
context 'when Module#name calls result in exceptions' do
let(:object_space_modules) { [invalid_class] }
let(:expected_warning) do
"Class#name from: #{invalid_class} raised an error: " \
"RuntimeError. #{Mutant::Env::SEMANTICS_MESSAGE}"
end
let(:invalid_class) do
Class.new do
def self.name
fail
end
end
end
after do
# Fix Class#name so other specs do not see this one
class << invalid_class
undef :name
def name
end
end
end
before { expect_warning }
include_examples 'bootstrap call'
end
context 'when requires are configured' do
let(:config) { super().with(requires: %w[foo bar]) }
before do
%w[foo bar].each do |component|
expect(Kernel).to receive(:require)
.with(component)
.and_return(true)
end
end
include_examples 'bootstrap call'
end
context 'when includes are configured' do
let(:config) { super().with(includes: %w[foo bar]) }
before do
%w[foo bar].each do |component|
expect($LOAD_PATH).to receive(:<<)
.with(component)
.and_return($LOAD_PATH)
end
end
include_examples 'bootstrap call'
end
context 'when Module#name does not return a String or nil' do
let(:object_space_modules) { [invalid_class] }
let(:invalid_class) do
Class.new do
def self.name
Object
end
end
end
let(:expected_warning) do
"Class#name from: #{invalid_class} " \
"returned Object. #{Mutant::Env::SEMANTICS_MESSAGE}"
end
after do
# Fix Class#name so other specs do not see this one
class << invalid_class
undef :name
def name
end
end
end
before { expect_warning }
include_examples 'bootstrap call'
end
context 'when scope matches expression' do
let(:object_space_modules) { [TestApp::Literal, TestApp::Empty] }
let(:match_expressions) { object_space_modules.map(&:name).map(&method(:parse_expression)) }
let(:matcher_config) do
super().with(match_expressions: match_expressions)
end
let(:expected_env) do
subjects = Mutant::Matcher::Scope.new(TestApp::Literal).call(Fixtures::TEST_ENV)
super().with(
matchable_scopes: [
Mutant::Scope.new(TestApp::Empty, match_expressions.last),
Mutant::Scope.new(TestApp::Literal, match_expressions.first)
],
mutations: subjects.flat_map(&:mutations),
subjects: subjects
)
end
include_examples 'bootstrap call'
end
end
end