Metric diven cleanups and break large classes

* Formal argument mutations are still very dirty :(
This commit is contained in:
Markus Schirp 2012-12-29 20:12:48 +01:00
parent 3026572b50
commit fce6d5c06e
23 changed files with 412 additions and 270 deletions

View file

@ -1,3 +1,3 @@
---
threshold: 22 # Todo bring down to ~20
threshold: 25 # Todo bring down to ~20
total_score: 902

View file

@ -1,2 +1,2 @@
---
threshold: 47.0
threshold: 44.0

View file

@ -10,8 +10,9 @@ UncommunicativeParameterName:
LargeClass:
max_methods: 10
exclude:
- "Mutant::Matcher::Method" # 13 methods
#- "Mutant::Matcher::Method" # 13 methods
- "Mutant::Reporter::CLI" # 16 methods TODO Reduce!
- "Mutant::CLI" # 19 methods and 7 ivars, TODO Reduce!
enabled: true
max_instance_variables: 3
UncommunicativeMethodName:
@ -50,7 +51,8 @@ UncommunicativeModuleName:
- !ruby/regexp /[0-9]$/
NestedIterators:
ignore_iterators: []
exclude: []
exclude:
- Mutant#self.define_singleton_subclass
enabled: true
max_allowed_nesting: 1
LongMethod:

View file

@ -13,12 +13,14 @@ require 'diff/lcs'
require 'diff/lcs/hunk'
require 'rspec'
module IceNine
class Freezer
class Rubinius
class AST < IceNine::Freezer::Object
class Node < IceNine::Freezer::Object
end
# Patch ice none to freeze nodes correctly
class IceNine::Freezer
# Rubinius namsepace
class Rubinius
# AST namespace
class AST < IceNine::Freezer::Object
# Node configuration
class Node < IceNine::Freezer::Object
end
end
end
@ -51,8 +53,6 @@ module Mutant
self
end
PID = Process.pid
end
require 'mutant/support/method_object'
@ -65,6 +65,8 @@ require 'mutant/mutation/filter/code'
require 'mutant/mutation/filter/whitelist'
require 'mutant/mutator/registry'
require 'mutant/mutator/util'
require 'mutant/mutator/util/array'
require 'mutant/mutator/util/symbol'
require 'mutant/mutator/node'
require 'mutant/mutator/node/literal'
require 'mutant/mutator/node/literal/boolean'
@ -81,8 +83,15 @@ require 'mutant/mutator/node/literal/nil'
require 'mutant/mutator/node/block'
require 'mutant/mutator/node/noop'
require 'mutant/mutator/node/send'
require 'mutant/mutator/node/arguments'
require 'mutant/mutator/node/define'
require 'mutant/mutator/node/formal_arguments_19'
require 'mutant/mutator/node/formal_arguments_19/default_mutations'
require 'mutant/mutator/node/formal_arguments_19/require_defaults'
require 'mutant/mutator/node/formal_arguments_19/pattern_argument_expansion'
require 'mutant/mutator/node/actual_arguments'
require 'mutant/mutator/node/pattern_arguments'
require 'mutant/mutator/node/pattern_variable'
require 'mutant/mutator/node/default_arguments'
require 'mutant/mutator/node/return'
require 'mutant/mutator/node/local_variable_assignment'
require 'mutant/mutator/node/iter_19'

View file

@ -30,12 +30,12 @@ module Mutant
# @api private
#
def self.wrap(scope, node)
name = scope.name.split('::').last
name = scope.name.split('::').last.to_sym
case scope
when ::Class
::Rubinius::AST::Class.new(0, name.to_sym, nil, node)
::Rubinius::AST::Class.new(0, name, nil, node)
when ::Module
::Rubinius::AST::Module.new(0, name.to_sym, node)
::Rubinius::AST::Module.new(0, name, node)
else
raise "Cannot wrap scope: #{scope.inspect}"
end

View file

@ -48,10 +48,22 @@ module Mutant
# @api private
#
def initialize(old, new)
@old, @new = self.class.lines(old), self.class.lines(new)
@old, @new = lines(old), lines(new)
@diffs = Diff::LCS.diff(@old, @new)
end
# Break up sorce into lines
#
# @param [String] source
#
# @return [Array<String>]
#
# @api private
#
def lines(source)
self.class.lines(source)
end
# Return length difference
#
# @return [Fixnum]

View file

@ -132,5 +132,6 @@ module Mutant
# @api private
#
abstract_method :run
end
end

View file

@ -55,6 +55,20 @@ module Mutant
self.class::MATCHER
end
# Return method names
#
# @param [Object] object
#
# @return [Enumerable<Symbol>]
#
# @api private
#
def self.method_names(object)
object.public_instance_methods(false) +
object.private_instance_methods(false) +
object.protected_instance_methods(false)
end
private
# Initialize object
@ -118,11 +132,7 @@ module Mutant
#
def method_names
singleton_class = scope.singleton_class
names =
singleton_class.public_instance_methods(false) +
singleton_class.private_instance_methods(false) +
singleton_class.protected_instance_methods(false)
names = self.class.method_names(singleton_class)
names.sort.reject do |name|
name.to_sym == :__class_init__
@ -158,12 +168,7 @@ module Mutant
def method_names
scope = self.scope
return [] unless scope.kind_of?(Module)
names =
scope.public_instance_methods(false) +
scope.private_instance_methods(false) +
scope.protected_instance_methods(false)
names = self.class.method_names(scope)
names.uniq.sort
end
end

View file

@ -189,9 +189,7 @@ module Mutant
# @api private
#
def run(mutator)
mutator.each(input) do |mutation|
emit(mutation)
end
mutator.new(input, method(:emit))
end
# Shortcut to create a new unfrozen duplicate of input

View file

@ -143,6 +143,7 @@ module Mutant
# @api private
#
alias_method :dup_node, :dup_input
end
end
end

View file

@ -0,0 +1,25 @@
module Mutant
class Mutator
class Node
# Mutator for arguments
class Arguments < self
handle(Rubinius::AST::ActualArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:array)
end
end
end
end
end

View file

@ -1,161 +1,6 @@
module Mutant
class Mutator
class Node
# Mutator for pattern arguments
class PatternVariable < self
handle(Rubinius::AST::PatternVariable)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:name)
end
end
# Mutantor for default arguments
class DefaultArguments < self
handle(Rubinius::AST::DefaultArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:arguments) do |argument|
argument.names = argument.arguments.map(&:name)
end
end
end
# Mutator for pattern arguments
class PatternArguments < self
handle(Rubinius::AST::PatternArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
Mutator.each(node.arguments.body) do |mutation|
dup = dup_node
dup.arguments.body = mutation
emit(dup)
end
end
# Test if mutation should be skipped
#
# @return [true]
# if mutation should be skipped
#
# @return [false]
# otherwise
#
# @api private
#
def allow?(object)
object.arguments.body.size >= 2
end
end
# Mutator for formal arguments
class FormatlArguments19 < self
private
handle(Rubinius::AST::FormalArguments19)
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
expand_pattern_args
emit_default_mutations
emit_required_defaults_mutation
emit_attribute_mutations(:required) do |mutation|
mutation.names = mutation.optional + mutation.required
end
end
# Emit default mutations
#
# @return [undefined]
#
# @api private
#
def emit_default_mutations
return unless node.defaults
emit_attribute_mutations(:defaults) do |mutation|
mutation.optional = mutation.defaults.names
mutation.names = mutation.required + mutation.optional
if mutation.defaults.names.empty?
mutation.defaults = nil
end
end
end
# Emit required defaults mutations
#
# @return [undefined]
#
# @api private
#
def emit_required_defaults_mutation
return unless node.defaults
arguments = node.defaults.arguments
arguments.each_index do |index|
names = arguments.take(index+1).map(&:name)
dup = dup_node
defaults = dup.defaults
defaults.arguments = defaults.arguments.drop(names.size)
names.each { |name| dup.optional.delete(name) }
dup.required.concat(names)
if dup.optional.empty?
dup.defaults = nil
end
emit(dup)
end
end
# Emit pattern args expansions
#
# @return [undefined]
#
# @api private
#
def expand_pattern_args
node.required.each_with_index do |argument, index|
next unless argument.kind_of?(Rubinius::AST::PatternArguments)
dup = dup_node
required = dup.required
required.delete_at(index)
argument.arguments.body.reverse.each do |node|
required.insert(index, node.name)
end
dup.names |= required
emit(dup)
end
end
end
# Mutator for arguments
class Arguments < self

View file

@ -0,0 +1,24 @@
module Mutant
class Mutator
class Node
# Mutantor for default arguments
class DefaultArguments < self
handle(Rubinius::AST::DefaultArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:arguments) do |argument|
argument.names = argument.arguments.map(&:name)
end
end
end
end
end
end

View file

@ -0,0 +1,40 @@
module Mutant
class Mutator
class Node
# Mutator for formal arguments in 1.9 mode
class FormalArguments19 < self
private
handle(Rubinius::AST::FormalArguments19)
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
run(DefaultMutations)
run(RequireDefaults)
run(PatternArgumentExpansion)
emit_required_mutations
end
# Emit required mutations
#
# @return [undefined]
#
# @api private
#
def emit_required_mutations
emit_attribute_mutations(:required) do |mutation|
mutation.names = mutation.optional + mutation.required
end
end
end
end
end
end

View file

@ -0,0 +1,32 @@
module Mutant
class Mutator
class Node
class FormalArguments19
# Mutator for default argument values
class DefaultMutations < Node
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
return unless node.defaults
emit_attribute_mutations(:defaults) do |mutation|
mutation.optional = mutation.defaults.names
mutation.names = mutation.required + mutation.optional
if mutation.defaults.names.empty?
mutation.defaults = nil
end
end
end
end
end
end
end
end

View file

@ -0,0 +1,35 @@
module Mutant
class Mutator
class Node
class FormalArguments19
# Mutator that expands pattern arguments
class PatternArgumentExpansion < Node
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
node.required.each_with_index do |argument, index|
next unless argument.kind_of?(Rubinius::AST::PatternArguments)
dup = dup_node
required = dup.required
required.delete_at(index)
argument.arguments.body.reverse.each do |node|
required.insert(index, node.name)
end
dup.names |= required
emit(dup)
end
end
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Mutant
class Mutator
class Node
class FormalArguments19
# Mutator for removing defaults and transform them into required arguments
class RequireDefaults < Node
private
# Emit mutants
#
# @return [undefined]
#
# @api private
#
def dispatch
return unless node.defaults
arguments = node.defaults.arguments
arguments.each_index do |index|
names = arguments.take(index+1).map(&:name)
dup = dup_node
defaults = dup.defaults
defaults.arguments = defaults.arguments.drop(names.size)
names.each { |name| dup.optional.delete(name) }
dup.required.concat(names)
if dup.optional.empty?
dup.defaults = nil
end
emit(dup)
end
end
end
end
end
end
end

View file

@ -0,0 +1,41 @@
module Mutant
class Mutator
class Node
# Mutator for pattern arguments
class PatternArguments < self
handle(Rubinius::AST::PatternArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
Mutator.each(node.arguments.body) do |mutation|
dup = dup_node
dup.arguments.body = mutation
emit(dup)
end
end
# Test if mutation should be skipped
#
# @return [true]
# if mutation should be skipped
#
# @return [false]
# otherwise
#
# @api private
#
def allow?(object)
object.arguments.body.size >= 2
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Mutant
class Mutator
class Node
# Mutator for pattern variable
class PatternVariable < self
handle(Rubinius::AST::PatternVariable)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:name)
end
end
end
end
end

View file

@ -26,7 +26,7 @@ module Mutant
#
# @return [undefined]
#
# @api pirvate
# @api private
#
def emit_receiver
unless self?

View file

@ -38,89 +38,6 @@ module Mutant
def new?(generated)
input != generated
end
# Mutators that mutates symbol inputs
class Symbol < self
handle(::Symbol)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_new { :"s#{Random.hex_string}" }
end
end
# Mutators that mutates an array of inputs
class Array < self
handle(::Array)
class Presence < Util
private
# Emit element presence mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
input.each_index do |index|
dup = dup_input
dup.delete_at(index)
emit(dup)
end
end
end
class Element < Util
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
input.each_with_index do |element, index|
dup = dup_input
Mutator.each(element).each do |mutation|
dup[index]=mutation
emit(dup)
end
end
end
end
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
run(Element)
run(Presence)
emit([])
end
end
end
end
end

View file

@ -0,0 +1,70 @@
module Mutant
class Mutator
class Util
# Mutators that mutates an array of inputs
class Array < self
handle(::Array)
class Presence < Util
private
# Emit element presence mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
input.each_index do |index|
dup = dup_input
dup.delete_at(index)
emit(dup)
end
end
end
class Element < Util
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
input.each_with_index do |element, index|
dup = dup_input
Mutator.each(element).each do |mutation|
dup[index]=mutation
emit(dup)
end
end
end
end
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
run(Element)
run(Presence)
emit([])
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Mutant
class Mutator
class Util
# Mutators that mutates symbol inputs
class Symbol < self
handle(::Symbol)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_new { :"s#{Random.hex_string}" }
end
end
end
end
end