free_mutant/spec/integration/mutant/corpus_spec.rb
Markus Schirp a6875c281c Add axiom to corpus test and add noinstall option
Axiom will currently not be killed on CI. Once I merge the parallel
stuff this is feasable.
2014-07-11 23:00:15 +00:00

233 lines
5.7 KiB
Ruby

# encoding: UTF-8
require 'parallel'
require 'spec_helper'
describe 'Mutant on ruby corpus' do
ROOT = Pathname.new(__FILE__).parent.parent.parent.parent
TMP = ROOT.join('tmp').freeze
before do
skip 'Corpus test is deactivated on 1.9.3' if RUBY_VERSION.eql?('1.9.3')
skip 'Corpus test is deactivated on RBX' if RUBY_ENGINE.eql?('rbx')
end
MUTEX = Mutex.new
class Project
include Adamantium, Anima.new(
:name,
:repo_uri,
:exclude,
:mutation_coverage,
:mutation_generation,
:namespace,
:expect_coverage
)
# Verify mutation coverage
#
# @return [self]
# if successufl
#
# @raise [Exception]
#
def verify_mutation_coverage
checkout
Dir.chdir(repo_path) do
Bundler.with_clean_env do
install_mutant
system(%W[bundle exec mutant -I lib -r #{name} --score #{expect_coverage} --use rspec #{namespace}*])
end
end
end
# Verify mutation generation
#
# @return [self]
# if successful
#
# @raise [Exception]
# otherwise
#
# rubocop:disable MethodLength
def verify_mutation_generation
checkout
start = Time.now
paths = Pathname.glob(repo_path.join('**/*.rb')).sort_by(&:size).reverse
total = Parallel.map(paths, finish: method(:finish), start: method(:start)) do |path|
count = 0
node =
begin
Parser::CurrentRuby.parse(path.read)
rescue EncodingError, ArgumentError
nil # Make rubocop happy
end
if node
Mutant::Mutator::Node.each(node) do
count += 1
end
end
count
end.inject(0, :+)
took = Time.now - start
puts format(
'Total Mutations/Time/Parse-Errors: %s/%0.2fs - %0.2f/s',
total,
took,
total / took
)
self
end
# Checkout repository
#
# @return [self]
#
# @api private
#
def checkout
return self if noinstall?
TMP.mkdir unless TMP.directory?
if repo_path.exist?
Dir.chdir(repo_path) do
system(%w[git pull -f origin master])
system(%w[git clean -f -d -x])
end
else
system(%W[git clone #{repo_uri} #{repo_path}])
end
self
end
memoize :checkout
private
# Install mutant
#
# @return [undefined]
#
# @api private
#
def install_mutant
return if noinstall?
relative = ROOT.relative_path_from(repo_path)
devtools = ROOT.join('Gemfile.devtools').read
devtools << "gem 'mutant', path: '#{relative}'\n"
devtools << "gem 'mutant-rspec', path: '#{relative}'\n"
File.write(repo_path.join('Gemfile.devtools'), devtools)
lockfile = repo_path.join('Gemfile.lock')
lockfile.delete if lockfile.exist?
system('bundle install')
end
# Return repository path
#
# @return [Pathname]
#
# @api private
#
def repo_path
TMP.join(name)
end
# Test if installation should be skipped
#
# @return [Boolean]
#
# @api private
#
def noinstall?
ENV.key?('NOINSTALL')
end
# Print start progress
#
# @param [Pathname] path
# @param [Fixnum] _index
# @param [Fixnum] count
#
# @return [undefined]
#
def start(path, _index)
MUTEX.synchronize do
puts format('Starting - %s', path)
end
end
# Print finish progress
#
# @param [Pathname] path
# @param [Fixnum] _index
# @param [Fixnum] count
#
# @return [undefined]
#
def finish(path, _index, count)
MUTEX.synchronize do
puts format('Mutations - %4i - %s', count, path)
end
end
# Helper method to execute system commands
#
# @param [Array<String>] arguments
#
# @api private
#
def system(arguments)
return if Kernel.system(*arguments)
if block_given?
yield
else
fail 'System command failed!'
end
end
LOADER = Morpher.build do
s(:block,
s(:guard, s(:primitive, Array)),
s(:map,
s(:block,
s(:guard, s(:primitive, Hash)),
s(:hash_transform,
s(:key_symbolize, :repo_uri, s(:guard, s(:primitive, String))),
s(:key_symbolize, :name, s(:guard, s(:primitive, String))),
s(:key_symbolize, :namespace, s(:guard, s(:primitive, String))),
s(:key_symbolize, :expect_coverage, s(:guard, s(:primitive, Float))),
s(:key_symbolize, :mutation_coverage,
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
s(:key_symbolize, :mutation_generation,
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
s(:key_symbolize, :exclude, s(:map, s(:guard, s(:primitive, String))))
),
s(:load_attribute_hash,
# NOTE: The domain param has no DSL currently!
Morpher::Evaluator::Transformer::Domain::Param.new(
Project,
[:repo_uri, :name, :exclude, :mutation_coverage, :mutation_generation]
)
)
)
)
)
end
ALL = LOADER.call(YAML.load_file(ROOT.join('spec', 'integrations.yml')))
end
Project::ALL.select(&:mutation_generation).each do |project|
specify "#{project.name} does not fail on mutation generation" do
project.verify_mutation_generation
end
end
Project::ALL.select(&:mutation_coverage).each do |project|
specify "#{project.name} does have expected mutation coverage" do
project.verify_mutation_coverage
end
end
end