Rename Runner to Killer

* Add working integration spec for rspec (mutant) killer.
This commit is contained in:
Markus Schirp 2012-08-14 12:27:56 +02:00
parent c9219b1f1e
commit 23ee68ac1e
19 changed files with 183 additions and 102 deletions

View file

@ -35,8 +35,8 @@ module Mutant
end
require 'mutant/random'
require 'mutant/runner'
require 'mutant/runner/rspec'
require 'mutant/killer'
require 'mutant/killer/rspec'
require 'mutant/mutator'
require 'mutant/mutator/registry'
require 'mutant/mutator/literal'

View file

@ -1,6 +1,6 @@
module Mutant
# Abstract runner for tests
class Runner
# Abstract runner for mutant killers
class Killer
include Immutable, Abstract
# Run runner

View file

@ -0,0 +1,96 @@
module Mutant
class Killer
# Simple runner for rspec tests
class Rspec < self
# Return error stream
#
# @return [StringIO]
#
# @api private
#
attr_reader :error_stream
# Return output stream
#
# @return [StringIO]
#
# @api private
#
attr_reader :output_stream
# Run block in clean rspec environment
#
# @return [self]
#
# @api private
#
def self.nest
original_world, original_configuration =
::RSpec.instance_variable_get(:@world),
::RSpec.instance_variable_get(:@configuration)
::RSpec.instance_variable_set(:@world,nil)
::RSpec.instance_variable_set(:@configuration,nil)
yield
self
ensure
::RSpec.instance_variable_set(:@world,original_world)
::RSpec.instance_variable_set(:@configuration,original_configuration)
end
private
# Initialize rspec runner
#
# @return [undefined]
#
# @api private
#
def initialize(*)
@error_stream, @output_stream = StringIO.new, StringIO.new
super
end
# Run rspec test
#
# @return [true]
# returns true when test is NOT successful and the mutant was killed
#
# @return [false]
# returns false otherwise
#
# @api private
#
def run
!RSpec::Core::Runner.run(command_line_arguments, error_stream, output_stream).zero?
end
# Return command line arguments
#
# @return [Array]
#
# @api private
#
def command_line_arguments
%W(
--fail-fast
) + Dir[filename_pattern]
end
# Return rspec filename pattern
#
# @return [String]
#
# @api private
#
# TODO: Add an option or be clever and only run affected specs.
#
def filename_pattern
'spec/unit/**/*_spec.rb'
end
end
end
end

View file

@ -1,69 +0,0 @@
module Mutant
class Runner
# Simple runner for rspec tests
class Rspec < self
# Return error stream
#
# @return [StringIO]
#
# @api private
#
def error_stream
StringIO.new
end
# Return output stream
#
# @return [StringIO]
#
# @api private
#
def output_stream
StringIO.new
end
private
# Run rspec test
#
# @return [true]
# returns true when test is NOT successful and the mutant was killed
#
# @return [false]
# returns false otherwise
#
# @api private
#
def run
!RSpec::Core::Runner.run(command_line,error_stream,output_stream).zero?
end
# Return command line
#
# @return [Array]
#
# @api private
#
def command_line
%W(
--fail-fast
) + Dir[filename_pattern]
end
# Return rspec filename pattern
#
# @return [String]
#
# @api private
#
# TODO: Add an option or be clever and only run affected specs.
#
def filename_pattern
'spec/unit/**/*_spec.rb'
end
memoize :output_stream, :error_stream
end
end
end

View file

@ -59,6 +59,20 @@ module Mutant
self
end
# Insert AST node under context
#
# @param [Rubinius::AST::Node] node
#
# @return [self]
#
# @api private
#
def insert(node)
Loader.load(context.root(node))
self
end
private
# Initialize subject
@ -76,19 +90,5 @@ module Mutant
def initialize(context, node)
@context, @node = context, node
end
# Insert AST node under context
#
# @param [Rubinius::AST::Node] node
#
# @return [self]
#
# @api private
#
def insert(node)
Loader.load(context.root(node))
self
end
end
end

View file

@ -7,11 +7,11 @@ class CodeLoadingSubject
end
describe Mutant, 'code loading' do
let(:context) { Mutant::Context::Constant.build(CodeLoadingSubject) }
let(:node) { 'def foo; :bar; end'.to_ast }
let(:root) { context.root(node) }
subject { Mutant::Loader.load(root) }
let(:context) { Mutant::Context::Constant.build("/some/path",CodeLoadingSubject) }
let(:node) { 'def foo; :bar; end'.to_ast }
let(:root) { context.root(node) }
subject { Mutant::Loader.load(root) }
before { subject }

View file

@ -0,0 +1,31 @@
require 'spec_helper'
describe Mutant,'rspec integration' do
around do |example|
Dir.chdir(TestApp.root) do
example.run
end
end
specify 'allows to run rspec with mutations' do
Mutant::Matcher::Method.parse('TestApp::Literal#string').each do |subject|
subject.each do |mutation|
Mutant::Killer::Rspec.nest do
runner = Mutant::Killer::Rspec.run(subject,mutation)
runner.killed?.should be(true)
end
end
end
Mutant::Matcher::Method.parse('TestApp::Literal#uncovered_string').each do |subject|
subject.each do |mutation|
Mutant::Killer::Rspec.nest do
runner = Mutant::Killer::Rspec.run(subject,mutation)
runner.killed?.should be(false)
end
end
end
end
end

View file

@ -5,6 +5,9 @@ require 'rspec'
# require spec support files and shared behavior
Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require f }
$: << File.join(TestApp.root,'lib')
require 'test_app'
require 'mutant'
RSpec.configure do |config|

5
spec/support/test_app.rb Normal file
View file

@ -0,0 +1,5 @@
module TestApp
def self.root
File.expand_path('../../../test_app', __FILE__)
end
end

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner,'.run' do
describe Mutant::Killer,'.run' do
subject { class_under_test.run(mutation_subject,mutant) }
let(:mutation_subject) { mock('Subject', :insert => nil, :reset => nil) }

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner,'#killed?' do
describe Mutant::Killer,'#killed?' do
subject { object.killed? }
let(:object) { class_under_test.run(mutation_subject,mutant) }

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner,'#mutant' do
describe Mutant::Killer,'#mutant' do
subject { object.mutant }
let(:object) { class_under_test.run(mutation_subject, mutant) }

View file

@ -1,10 +1,10 @@
require 'spec_helper'
describe Mutant::Runner::Rspec, '.run' do
describe Mutant::Killer::Rspec, '.run' do
subject { object.run(context, mutant) }
let(:context) { mock('Context') }
let(:mutant) { mock('Mutant') }
let(:context) { mock('Context') }
let(:mutant) { mock('Mutant') }
let(:object) { described_class }

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner::Rspec,'#error_stream' do
describe Mutant::Killer::Rspec,'#error_stream' do
subject { object.error_stream }
let(:object) { described_class.run(mutation_subject,mutant) }

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner::Rspec,'#output_stream' do
describe Mutant::Killer::Rspec,'#output_stream' do
subject { object.output_stream }
let(:object) { described_class.run(mutation_subject,mutant) }

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant::Runner,'#subject' do
describe Mutant::Killer,'#subject' do
subject { object.subject }
let(:object) { class_under_test.run(mutation_subject, mutant) }

View file

@ -10,6 +10,10 @@ describe Mutant::Matcher::Method, '#each' do
node
end
define_method(:method) do
TestApp::Literal.instance_method(:string)
end
define_method(:constant) do
::SampleSubjects::ExampleModule
end

View file

@ -5,10 +5,22 @@ module TestApp
true
end
def self.freeze
raise
end
def string
'string'
end
def uncovered_string
'string'
end
def self.string
'string'
end
def symbol
:symbol
end

View file

@ -1,7 +1,6 @@
# encoding: utf-8
require 'rspec'
require 'test_app'
# require spec support files and shared behavior