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 threshold: 18
total_score: 1171 total_score: 1167

View file

@ -57,13 +57,15 @@ module Mutant
# #
# @param [#to_s] logical_name # @param [#to_s] logical_name
# #
# @return [undefined] # @return [Bool]
# true if successful and false if feature already loaded
def require(logical_name) def require(logical_name)
logical_name = logical_name.to_s logical_name = logical_name.to_s
@original.call(logical_name) loaded = @original.call(logical_name)
return unless include?(logical_name) return loaded unless include?(logical_name)
@zombified << logical_name @zombified << logical_name
zombify(find(logical_name)) zombify(find(logical_name))
true
end end
# Find file by logical path # 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 # require semantics Zombifier relies on in a way we can avoid having to
# mock around everywhere to test every detail. # mock around everywhere to test every detail.
class RubyVM class RubyVM
include Concord.new(:expected_events) include Concord::Public.new(:expected_events)
# An event being observed by the VM handlers # An event being observed by the VM handlers
class EventObservation class EventObservation
@ -14,7 +14,8 @@ module MutantSpec
class EventExpectation class EventExpectation
include AbstractType, Anima.new( include AbstractType, Anima.new(
:expected_payload, :expected_payload,
:trigger_requires :trigger_requires,
:return_value
) )
DEFAULTS = IceNine.deep_freeze(trigger_requires: []) DEFAULTS = IceNine.deep_freeze(trigger_requires: [])
@ -29,12 +30,12 @@ module MutantSpec
end end
trigger_requires.each(&vm.method(:require)) trigger_requires.each(&vm.method(:require))
self
end end
private private
abstract_method :advance_vm
def match?(observation) def match?(observation)
observation.type.eql?(self.class) && observation.payload.eql?(expected_payload) observation.type.eql?(self.class) && observation.payload.eql?(expected_payload)
end end
@ -51,7 +52,6 @@ module MutantSpec
# A fake implementation of Kernel#require # A fake implementation of Kernel#require
def require(logical_name) def require(logical_name)
handle_event(EventObservation.new(EventExpectation::Require, logical_name: logical_name)) handle_event(EventObservation.new(EventExpectation::Require, logical_name: logical_name))
self
end end
# A fake implementation of Kernel#eval # A fake implementation of Kernel#eval
@ -64,7 +64,6 @@ module MutantSpec
source_location: location source_location: location
) )
) )
self
end end
# Test if VM events where fully processed # Test if VM events where fully processed
@ -77,7 +76,7 @@ module MutantSpec
def handle_event(observation) def handle_event(observation)
fail "Unexpected event: #{observation.type} / #{observation.payload}" if expected_events.empty? 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 end
end # MutantSpec end # MutantSpec

View file

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