Skip to content
Browse files

Fix constant scope mutations are emitted in

  • Loading branch information...
1 parent 4f823e7 commit 386a2bc2dfba755ce5d4c80aa50dad88ccb80a86 Markus Schirp committed Dec 12, 2012
View
2 Changelog.md
@@ -8,6 +8,8 @@
* [feature] Mutate define and block arguments
* [feature] Mutate block arguments, inklusive pattern args
* [feature] Recurse into block bodies
+* [change] Unvendor inflector use mbj-inflector from rubygems
+* [fixed] Insert mutations at correct constant scope
* [fixed] Crash on mutating yield, added a noop for now
* [fixed] Crash on singleton methods defined on other than constants or self
View
1 Gemfile.devtools
@@ -8,6 +8,7 @@ group :guard do
gem 'guard', '~> 1.5.4'
gem 'guard-bundler', '~> 1.0.0'
gem 'guard-rspec', '~> 2.1.1'
+ gem 'rb-inotify', :git => 'https://github.com/mbj/rb-inotify'
end
group :benchmarks do
View
2 Guardfile
@@ -4,7 +4,7 @@ guard :bundler do
watch('Gemfile')
end
-guard :rspec, :all_on_start => false do
+guard :rspec, :all_on_start => false, :all_after_pass => false do
# run all specs if the spec_helper or supporting files files are modified
watch('spec/spec_helper.rb') { 'spec/unit' }
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec/unit' }
View
69 lib/mutant/context/scope.rb
@@ -2,54 +2,59 @@ module Mutant
class Context
# Scope context for mutation (Class or Module)
class Scope < self
- include Adamantium::Flat, AbstractType, Equalizer.new(:scope, :source_path)
+ include Adamantium::Flat, Equalizer.new(:scope, :source_path)
- # Class context for mutation
- class Class < self
- SCOPE_CLASS = Rubinius::AST::ClassScope
- KEYWORD = 'class'.freeze
- end
-
- # Module context for mutation
- class Module < self
- SCOPE_CLASS = Rubinius::AST::ModuleScope
- KEYWORD = 'module'.freeze
+ # Return AST wrapping mutated node
+ #
+ # @return [Rubinius::AST::Script]
+ #
+ # @api private
+ #
+ def root(node)
+ nesting.reverse.inject(node) do |current, scope|
+ self.class.wrap(scope, current)
+ end
end
- TABLE = {
- ::Module => Module,
- ::Class => Class
- }.freeze
-
- # Build scope context from class or module
+ # Wrap node into ast node
#
- # @param [String] source_path
+ # @param [Class, Module] scope
+ # @param [Rubinius::AST::Node] node
#
- # @param [::Class|::Module] scope
+ # @return [Rubinius::AST::Class]
+ # if scope is of kind Class
#
- # @return [Context::Scope]
+ # @return [Rubinius::AST::Module]
+ # if scope is of kind module
#
# @api private
#
- def self.build(scope, source_path)
- scope_class = scope.class
- klass = TABLE.fetch(scope_class) do
- raise ArgumentError, "Can only build mutation scope from class or module got: #{scope_class.inspect}"
- end.new(scope, source_path)
+ def self.wrap(scope, node)
+ name = scope.name.split('::').last
+ case scope
+ when ::Class
+ ::Rubinius::AST::Class.new(0, name.to_sym, nil, node)
+ when ::Module
+ ::Rubinius::AST::Module.new(0, name.to_sym, node)
+ else
+ raise "Cannot wrap scope: #{scope.inspect}"
+ end
end
- # Return AST wrapping mutated node
+ # Return nesting
#
- # @return [Rubinius::AST::Script]
+ # @return [Enumerable<Class,Module>]
#
# @api private
#
- def root(node)
- root = root_ast
- block = Rubinius::AST::Block.new(1, [node])
- root.body = scope_class.new(1, root.name, block)
- root
+ def nesting
+ const = ::Object
+ name_nesting.each_with_object([]) do |name, nesting|
+ const = const.const_get(name)
+ nesting << const
+ end
end
+ memoize :nesting
# Return unqualified name of scope
#
View
2 lib/mutant/matcher/method.rb
@@ -103,7 +103,7 @@ def initialize(scope, method)
# @api private
#
def context
- Context::Scope.build(scope, source_path)
+ Context::Scope.new(scope, source_path)
end
# Return full ast
View
8 spec/integration/mutant/loader_spec.rb
@@ -7,11 +7,11 @@ def x
end
describe Mutant, 'code loading' do
- let(:context) { Mutant::Context::Scope.build(CodeLoadingSubject,"/some/path") }
- let(:node) { 'def foo; :bar; end'.to_ast }
- let(:root) { context.root(node) }
+ let(:context) { Mutant::Context::Scope.new(CodeLoadingSubject, "/some/path") }
+ let(:node) { 'def foo; :bar; end'.to_ast }
+ let(:root) { context.root(node) }
- subject { Mutant::Loader::Eval.run(root) }
+ subject { Mutant::Loader::Eval.run(root) }
before { subject }
View
29 spec/unit/mutant/context/scope/class_methods/build_spec.rb
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-describe Mutant::Context::Scope, '.build' do
- subject { described_class.build(constant, path) }
-
- let(:object) { described_class }
- let(:context) { mock('Context') }
- let(:path) { mock('Path') }
-
- context 'when constant is a module' do
- let(:constant) { Module.new }
-
- it { should be_kind_of(described_class::Module) }
- end
-
- context 'when constant is a class' do
- let(:constant) { Class.new }
-
- it { should be_kind_of(described_class::Class) }
- end
-
- context 'when constant is not a class nor a module' do
- let(:constant) { Object.new }
-
- it 'should raise error' do
- expect { subject }.to raise_error(ArgumentError, 'Can only build mutation scope from class or module got: Object')
- end
- end
-end
View
28 spec/unit/mutant/context/scope/root_spec.rb
@@ -3,20 +3,32 @@
describe Mutant::Context::Scope, '#root' do
subject { object.root(node) }
- let(:object) { described_class.build(TestApp::Literal, path) }
+ let(:object) { described_class.new(TestApp::Literal, path) }
let(:path) { mock('Path') }
- let(:node) { mock('Node') }
+ let(:node) { ':node'.to_ast }
let(:scope) { subject.body }
let(:scope_body) { scope.body }
- it 'should wrap the ast under constant' do
- scope.should be_kind_of(Rubinius::AST::ClassScope)
+ let(:expected_source) do
+ ToSource.to_source(<<-RUBY.to_ast)
+ module TestApp
+ class Literal
+ :node
+ end
+ end
+ RUBY
end
- it 'should place the ast under scope inside of block' do
- scope_body.should be_a(Rubinius::AST::Block)
- scope_body.array.should eql([node])
- scope_body.array.first.should be(node)
+ let(:generated_source) do
+ ToSource.to_source(subject)
+ end
+
+ let(:round_tripped_source) do
+ ToSource.to_source(expected_source.to_ast)
+ end
+
+ it 'should create correct source' do
+ generated_source.should eql(expected_source)
end
end
View
4 spec/unit/mutant/context/scope/unqualified_name_spec.rb
@@ -6,7 +6,7 @@
let(:path) { mock('Path') }
context 'with top level constant name' do
- let(:object) { described_class.build(TestApp, path) }
+ let(:object) { described_class.new(TestApp, path) }
it 'should return the unqualified name' do
should eql('TestApp')
@@ -16,7 +16,7 @@
end
context 'with scoped constant name' do
- let(:object) { described_class.build(TestApp::Literal, path) }
+ let(:object) { described_class.new(TestApp::Literal, path) }
it 'should return the unqualified name' do
should eql('Literal')

0 comments on commit 386a2bc

Please sign in to comment.
Something went wrong with that request. Please try again.