Fix zombifier require to return loaded flag

* Ruby 2.3 uses Kernel#require to expand autoloads, and autload constant
  propagation only is done on #require returning true.
* The zombifier used to return incorrect falsy/truthy values.
* This would never have happened on a static language
This commit is contained in:
Markus Schirp 2016-01-24 21:08:56 +00:00
parent d292dc8028
commit 7e670bafaa
4 changed files with 29 additions and 25 deletions

View file

@ -1,3 +1,3 @@
---
threshold: 18
total_score: 1171
total_score: 1167

View file

@ -57,13 +57,15 @@ module Mutant
#
# @param [#to_s] logical_name
#
# @return [undefined]
# @return [Bool]
# true if successful and false if feature already loaded
def require(logical_name)
logical_name = logical_name.to_s
@original.call(logical_name)
return unless include?(logical_name)
loaded = @original.call(logical_name)
return loaded unless include?(logical_name)
@zombified << logical_name
zombify(find(logical_name))
true
end
# Find file by logical path

View file

@ -3,7 +3,7 @@ module MutantSpec
# require semantics Zombifier relies on in a way we can avoid having to
# mock around everywhere to test every detail.
class RubyVM
include Concord.new(:expected_events)
include Concord::Public.new(:expected_events)
# An event being observed by the VM handlers
class EventObservation
@ -14,7 +14,8 @@ module MutantSpec
class EventExpectation
include AbstractType, Anima.new(
:expected_payload,
:trigger_requires
:trigger_requires,
:return_value
)
DEFAULTS = IceNine.deep_freeze(trigger_requires: [])
@ -29,12 +30,12 @@ module MutantSpec
end
trigger_requires.each(&vm.method(:require))
self
end
private
abstract_method :advance_vm
def match?(observation)
observation.type.eql?(self.class) && observation.payload.eql?(expected_payload)
end
@ -51,7 +52,6 @@ module MutantSpec
# A fake implementation of Kernel#require
def require(logical_name)
handle_event(EventObservation.new(EventExpectation::Require, logical_name: logical_name))
self
end
# A fake implementation of Kernel#eval
@ -64,7 +64,6 @@ module MutantSpec
source_location: location
)
)
self
end
# Test if VM events where fully processed
@ -77,7 +76,7 @@ module MutantSpec
def handle_event(observation)
fail "Unexpected event: #{observation.type} / #{observation.payload}" if expected_events.empty?
expected_events.slice!(0).handle(self, observation)
expected_events.slice!(0).handle(self, observation).return_value
end
end
end # MutantSpec

View file

@ -5,7 +5,10 @@ RSpec.describe Mutant::Zombifier do
let(:require_highjack) do
lambda do |block|
original = ruby_vm.method(:require)
allow(ruby_vm).to receive(:require, &block)
allow(ruby_vm).to receive(:require) do |argument|
return_value = ruby_vm.expected_events.first.return_value
expect(block.call(argument)).to be(return_value)
end
original
end
end
@ -28,7 +31,8 @@ RSpec.describe Mutant::Zombifier do
MutantSpec::RubyVM::EventExpectation::Require.new(
expected_payload: {
logical_name: 'project'
}
},
return_value: true
),
MutantSpec::RubyVM::EventExpectation::Eval.new(
expected_payload: {
@ -36,18 +40,21 @@ RSpec.describe Mutant::Zombifier do
source: "module Zombie\n module Project\n end\nend",
source_location: 'a/project.rb'
},
trigger_requires: %w[foo bar]
trigger_requires: %w[foo bar],
return_value: nil
),
MutantSpec::RubyVM::EventExpectation::Require.new(
expected_payload: {
logical_name: 'foo'
},
trigger_requires: %w[bar]
trigger_requires: %w[bar],
return_value: true
),
MutantSpec::RubyVM::EventExpectation::Require.new(
expected_payload: {
logical_name: 'bar'
}
},
return_value: true
),
MutantSpec::RubyVM::EventExpectation::Eval.new(
expected_payload: {
@ -55,23 +62,19 @@ RSpec.describe Mutant::Zombifier do
source: "module Zombie\n module Bar\n end\nend",
source_location: 'b/bar.rb'
},
trigger_requires: %w[]
trigger_requires: %w[],
return_value: nil
),
MutantSpec::RubyVM::EventExpectation::Require.new(
expected_payload: {
logical_name: 'bar'
}
},
return_value: false
)
]
)
end
let(:require_effects) do
{
'project' => { requires: [] }
}
end
let(:file_entries) do
{
'a/project.rb' => { file: true, contents: 'module Project; end' },
@ -100,7 +103,7 @@ RSpec.describe Mutant::Zombifier do
expect(apply).to be(described_class)
end
it 'consumes walks the VM through expected steps' do
it 'walks the VM through expected steps' do
expect { apply }.to change(ruby_vm, :done?).from(false).to(true)
end