Initial rspec killer with "longes description prefix match"
This commit is contained in:
parent
ad0ea265a2
commit
f70dc6703f
11 changed files with 168 additions and 41 deletions
2
Gemfile
2
Gemfile
|
@ -6,6 +6,8 @@ gemspec
|
|||
|
||||
gem 'mutant', path: '.'
|
||||
|
||||
gem 'rspec-core', path: '../rspec-core'
|
||||
|
||||
group :development, :test do
|
||||
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
|
||||
end
|
||||
|
|
|
@ -19,6 +19,7 @@ require 'diff/lcs/hunk'
|
|||
require 'rspec'
|
||||
require 'anima'
|
||||
require 'concord'
|
||||
require 'rspec'
|
||||
|
||||
# Library namespace
|
||||
module Mutant
|
||||
|
|
|
@ -244,6 +244,9 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def add_strategies(opts)
|
||||
opts.separator ''
|
||||
opts.separator 'Strategies:'
|
||||
|
||||
opts.on('--static-success', 'does succeed on all mutations') do
|
||||
set_strategy Strategy::Static::Success.new
|
||||
end
|
||||
|
|
|
@ -89,6 +89,17 @@ module Mutant
|
|||
scope.name
|
||||
end
|
||||
|
||||
# Return match prefixes
|
||||
#
|
||||
# @return [Enumerable<String>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def match_prefixes
|
||||
name_nesting.reverse
|
||||
end
|
||||
memoize :match_prefixes
|
||||
|
||||
# Return scope wrapped by context
|
||||
#
|
||||
# @return [::Module|::Class]
|
||||
|
|
|
@ -98,6 +98,16 @@ module Mutant
|
|||
@runtime = times.real
|
||||
end
|
||||
|
||||
# Return subject
|
||||
#
|
||||
# @return [Subject]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def subject
|
||||
mutation.subject
|
||||
end
|
||||
|
||||
# Run killer
|
||||
#
|
||||
# @return [true]
|
||||
|
|
|
@ -19,36 +19,72 @@ module Mutant
|
|||
#
|
||||
def run
|
||||
mutation.insert
|
||||
# TODO: replace with real streams from configuration
|
||||
require 'stringio'
|
||||
# Note: We assume interesting output from a failed rspec run is stderr.
|
||||
rspec_err = StringIO.new
|
||||
|
||||
exit_code = ::RSpec::Core::Runner.run(cli_arguments, nil, rspec_err)
|
||||
groups = example_groups
|
||||
|
||||
killed = !exit_code.zero?
|
||||
|
||||
if killed and mutation.should_survive?
|
||||
rspec_err.rewind
|
||||
|
||||
puts "#{mutation.class} test failed."
|
||||
puts 'RSpec stderr:'
|
||||
puts rspec_err.read
|
||||
unless groups
|
||||
$stderr.puts "No rspec example groups found for: #{match_prefixes.join(', ')}"
|
||||
return false
|
||||
end
|
||||
|
||||
killed
|
||||
strategy.configuration.reporter.report(groups.length, nil) do |reporter|
|
||||
example_groups.each do |group|
|
||||
return true unless group.run(reporter)
|
||||
end.all?
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Return command line arguments
|
||||
# Return match prefixes
|
||||
#
|
||||
# @return [Array]
|
||||
# @return [Enumerble<String>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def cli_arguments
|
||||
%W(
|
||||
--fail-fast
|
||||
) + strategy.spec_files(mutation.subject)
|
||||
def match_prefixes
|
||||
subject.match_prefixes
|
||||
end
|
||||
|
||||
# Return example groups
|
||||
#
|
||||
# @return [Array<RSpec::Example>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def example_groups
|
||||
match_prefixes.each do |match_expression|
|
||||
example_groups = find_with(match_expression)
|
||||
return example_groups unless example_groups.empty?
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Return example groups that match expression
|
||||
#
|
||||
# @param [String] match_expression
|
||||
#
|
||||
# @return [Enumerable<String>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def find_with(match_expression)
|
||||
all_example_groups.select do |example_group|
|
||||
example_group.description.start_with?(match_expression)
|
||||
end
|
||||
end
|
||||
|
||||
DELIMITERS = /::|#/.freeze
|
||||
|
||||
# Return all example groups
|
||||
#
|
||||
# @return [Enumerable<RSpec::Example>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def all_example_groups
|
||||
strategy.example_groups
|
||||
end
|
||||
|
||||
end # Rspec
|
||||
|
|
|
@ -15,8 +15,61 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def setup
|
||||
output = StringIO.new
|
||||
configuration.error_stream = output
|
||||
configuration.output_stream = output
|
||||
options.configure(configuration)
|
||||
configuration.load_spec_files
|
||||
self
|
||||
end
|
||||
memoize :setup
|
||||
|
||||
# Return configuration
|
||||
#
|
||||
# @return [RSpec::Core::Configuration]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def configuration
|
||||
RSpec::Core::Configuration.new
|
||||
end
|
||||
memoize :configuration, :freezer => :noop
|
||||
|
||||
# Return example groups
|
||||
#
|
||||
# @return [Enumerable<RSpec::Core::ExampleGroup>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def example_groups
|
||||
world.example_groups
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Return world
|
||||
#
|
||||
# @return [RSpec::Core::World]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def world
|
||||
RSpec.world
|
||||
end
|
||||
memoize :world, :freezer => :noop
|
||||
|
||||
# Return options
|
||||
#
|
||||
# @return [RSpec::Core::ConfigurationOptions]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def options
|
||||
options = RSpec::Core::ConfigurationOptions.new(%w(--fail-fast spec))
|
||||
options.parse_options
|
||||
options
|
||||
end
|
||||
memoize :options, :freezer => :noop
|
||||
|
||||
end # Rspec
|
||||
end # Strategy
|
||||
|
|
|
@ -46,7 +46,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def identification
|
||||
"#{subtype}:#{source_path}:#{source_line}"
|
||||
"#{match_expression}:#{source_path}:#{source_line}"
|
||||
end
|
||||
memoize :identification
|
||||
|
||||
|
@ -84,16 +84,26 @@ module Mutant
|
|||
end
|
||||
memoize :original_root
|
||||
|
||||
private
|
||||
|
||||
# Return subtype identifier
|
||||
# Return match expression
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :subtype
|
||||
private :subtype
|
||||
abstract_method :match_expression
|
||||
|
||||
# Return match prefixes
|
||||
#
|
||||
# @return [Enumerable<String>]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def match_prefixes
|
||||
context.match_prefixes.dup << match_expression
|
||||
end
|
||||
memoize :match_prefixes
|
||||
|
||||
private
|
||||
|
||||
# Return neutral mutation
|
||||
#
|
||||
|
|
|
@ -27,6 +27,16 @@ module Mutant
|
|||
node.children[self.class::NAME_INDEX]
|
||||
end
|
||||
|
||||
# Return match expression
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def match_expression
|
||||
"#{context.identification}#{self.class::SYMBOL}#{name}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Return mutations
|
||||
|
@ -54,16 +64,6 @@ module Mutant
|
|||
context.scope
|
||||
end
|
||||
|
||||
# Return subtype identifier
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def subtype
|
||||
"#{context.identification}#{self.class::SYMBOL}#{name}"
|
||||
end
|
||||
|
||||
end # Method
|
||||
end # Subject
|
||||
end # Mutant
|
||||
|
|
|
@ -10,11 +10,8 @@ describe Mutant, 'rspec integration' do
|
|||
end
|
||||
end
|
||||
|
||||
let(:strategy) { Mutant::Strategy::Rspec::DM2 }
|
||||
|
||||
pending 'allows to kill mutations' do
|
||||
cli = 'bundle exec mutant --rspec ::TestApp::Literal#string'
|
||||
Kernel.system(cli).should be(true)
|
||||
specify 'it allows to kill mutations' do
|
||||
Kernel.system('bundle exec mutant --rspec ::TestApp::Literal#string').should be(true)
|
||||
end
|
||||
|
||||
pending 'fails to kill mutations when they are not covered' do
|
|
@ -4,6 +4,10 @@ require 'spec_helper'
|
|||
|
||||
describe Mutant::Killer::Rspec, '.new' do
|
||||
|
||||
before do
|
||||
pending 'dactivated'
|
||||
end
|
||||
|
||||
subject { object.new(strategy, mutation) }
|
||||
|
||||
let(:context) { double('Context') }
|
||||
|
|
Loading…
Add table
Reference in a new issue