Skip to content

Commit

Permalink
Add config object to mutator state
Browse files Browse the repository at this point in the history
Next commits will provide config object from callsides. The config
object is currently empty.

The config object is passed to all children mutators. Currently no
mutator public interface was modified so no spec changes. With next
commits we have public visible stuff so specs will be adjusted.
  • Loading branch information
mbj committed Dec 7, 2013
1 parent b132b46 commit fc426be
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 126 deletions.
55 changes: 46 additions & 9 deletions lib/mutant/mutator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ module Mutant
class Mutator
include Adamantium::Flat, AbstractType

# Mutator configuration
class Config
include Anima.new
end

# Context of a mutation
class Context
include Concord::Public.new(:config, :parent, :input)
end

# Run mutator on input
#
# @param [Parser::AST::Node] node
Expand All @@ -15,7 +25,9 @@ class Mutator
#
def self.each(input, parent = nil, &block)
return to_enum(__method__, input, parent) unless block_given?
Registry.lookup(input).new(input, parent, block)

context = Context.new(Config.new({}), parent, input)
Registry.lookup(input).new(context, block)

self
end
Expand Down Expand Up @@ -45,41 +57,66 @@ def self.identity(object)
object
end

# Return parent mutator
#
# @return [Mutator]
# if parent mutator is present
#
# @return [nil]
# otherwise
#
# @api private
#
def parent
context.parent
end

# Return input
#
# @return [Object]
# @return [Config]
#
# @api private
#
attr_reader :input
def config
context.config
end

# Return input
#
# @return [Object]
#
# @api private
#
attr_reader :parent
def input
context.input
end

private

# Initialize object
#
# @param [Object] input
# @param [Object] parent
# @param [Context] context
# @param [#call(node)] block
#
# @return [undefined]
#
# @api private
#
def initialize(input, parent, block)
@input, @parent, @block = input, parent, block
def initialize(context, block)
@context, @block = context, block
@seen = Set.new
guard(input)
dispatch
end

# Return context
#
# @return [Context]
#
# @api private
#
attr_reader :context

# Test if generated object is not guarded from emmitting
#
# @param [Object] object
Expand Down Expand Up @@ -191,7 +228,7 @@ def emit!(node)
# @api private
#
def run(mutator)
mutator.new(input, self, method(:emit))
mutator.new(Context.new(config, self, input), method(:emit))
end

# Shortcut to create a new unfrozen duplicate of input
Expand Down
8 changes: 5 additions & 3 deletions lib/mutant/mutator/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ class Util < self
#
# @api private
#
def self.each(object, parent, &block)
return to_enum(__method__, object, parent) unless block_given?
def self.each(input, parent, &block)
return to_enum(__method__, input, parent) unless block_given?

new(object, parent, block)
context = Context.new(Config.new({}), parent, input)

new(context, block)

self
end
Expand Down
59 changes: 0 additions & 59 deletions spec/unit/mutant/mutator/emit_new_spec.rb

This file was deleted.

55 changes: 0 additions & 55 deletions spec/unit/mutant/mutator/emit_spec.rb

This file was deleted.

94 changes: 94 additions & 0 deletions spec/unit/mutant/mutator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# encoding: utf-8

require 'spec_helper'

describe Mutant::Mutator do

let(:object) { class_under_test.new(context, block) }

let(:context) { described_class::Context.new(config, parent, input) }
let(:block) { Block.new }
let(:input) { :input }
let(:parent) { :parent }
let(:config) { double('Config') }


class Block
attr_reader :arguments

def called?
defined?(@arguments)
end

def call(*arguments)
@arguments = arguments
end
end

let(:class_under_test) do
Class.new(described_class) do
def dispatch
# noop
end
end
end

describe '#emit' do

subject { object.send(:emit, generated) }

context 'with generated that is not equal to input' do
let(:generated) { :generated }

it 'should call block' do
subject
block.should be_called
end

it 'should call block with generated' do
subject
block.arguments.should eql([generated])
end
end

context 'with generated object that is equal to input' do
let(:generated) { input }

it 'should not call block' do
subject
block.should_not be_called
end
end
end

describe '#emit_new' do
subject { object.send(:emit_new) { generated } }

context 'when new object generated' do
let(:generated) { :generated }

it 'should call block' do
subject
block.should be_called
end

it 'should call block with generated object' do
subject
block.arguments.should eql([generated])
end
end

context 'when new AST could not be generated' do
let(:generated) { input }

it 'should raise error' do
expect do
subject
end.to raise_error(
RuntimeError,
'New AST could not be generated after 3 attempts'
)
end
end
end
end

0 comments on commit fc426be

Please sign in to comment.