diff --git a/config/flay.yml b/config/flay.yml index 8bc6e0fd..4b389c95 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- -threshold: 22 # Todo bring down to ~20 +threshold: 25 # Todo bring down to ~20 total_score: 902 diff --git a/config/flog.yml b/config/flog.yml index 1cb47d52..5704a723 100644 --- a/config/flog.yml +++ b/config/flog.yml @@ -1,2 +1,2 @@ --- -threshold: 47.0 +threshold: 44.0 diff --git a/config/site.reek b/config/site.reek index f3007ea5..b0fe68ec 100644 --- a/config/site.reek +++ b/config/site.reek @@ -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: diff --git a/lib/mutant.rb b/lib/mutant.rb index 4ae4a189..e72910b6 100644 --- a/lib/mutant.rb +++ b/lib/mutant.rb @@ -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' diff --git a/lib/mutant/context/scope.rb b/lib/mutant/context/scope.rb index be99ce9f..2394e980 100644 --- a/lib/mutant/context/scope.rb +++ b/lib/mutant/context/scope.rb @@ -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 diff --git a/lib/mutant/differ.rb b/lib/mutant/differ.rb index 06fde32c..43bfdcb2 100644 --- a/lib/mutant/differ.rb +++ b/lib/mutant/differ.rb @@ -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] + # + # @api private + # + def lines(source) + self.class.lines(source) + end + # Return length difference # # @return [Fixnum] diff --git a/lib/mutant/killer.rb b/lib/mutant/killer.rb index d4bc5afa..afee3314 100644 --- a/lib/mutant/killer.rb +++ b/lib/mutant/killer.rb @@ -132,5 +132,6 @@ module Mutant # @api private # abstract_method :run + end end diff --git a/lib/mutant/matcher/scope_methods.rb b/lib/mutant/matcher/scope_methods.rb index 862abdd3..6489e694 100644 --- a/lib/mutant/matcher/scope_methods.rb +++ b/lib/mutant/matcher/scope_methods.rb @@ -55,6 +55,20 @@ module Mutant self.class::MATCHER end + # Return method names + # + # @param [Object] object + # + # @return [Enumerable] + # + # @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 diff --git a/lib/mutant/mutator.rb b/lib/mutant/mutator.rb index 45bed09b..5ea7d85b 100644 --- a/lib/mutant/mutator.rb +++ b/lib/mutant/mutator.rb @@ -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 diff --git a/lib/mutant/mutator/node.rb b/lib/mutant/mutator/node.rb index e80e11c9..d543d51d 100644 --- a/lib/mutant/mutator/node.rb +++ b/lib/mutant/mutator/node.rb @@ -143,6 +143,7 @@ module Mutant # @api private # alias_method :dup_node, :dup_input + end end end diff --git a/lib/mutant/mutator/node/actual_arguments.rb b/lib/mutant/mutator/node/actual_arguments.rb new file mode 100644 index 00000000..7b2e6477 --- /dev/null +++ b/lib/mutant/mutator/node/actual_arguments.rb @@ -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 diff --git a/lib/mutant/mutator/node/arguments.rb b/lib/mutant/mutator/node/arguments.rb index b9a3a0b5..7b2e6477 100644 --- a/lib/mutant/mutator/node/arguments.rb +++ b/lib/mutant/mutator/node/arguments.rb @@ -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 diff --git a/lib/mutant/mutator/node/default_arguments.rb b/lib/mutant/mutator/node/default_arguments.rb new file mode 100644 index 00000000..bf885150 --- /dev/null +++ b/lib/mutant/mutator/node/default_arguments.rb @@ -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 diff --git a/lib/mutant/mutator/node/formal_arguments_19.rb b/lib/mutant/mutator/node/formal_arguments_19.rb new file mode 100644 index 00000000..76c12e41 --- /dev/null +++ b/lib/mutant/mutator/node/formal_arguments_19.rb @@ -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 diff --git a/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb b/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb new file mode 100644 index 00000000..fdb5e686 --- /dev/null +++ b/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb @@ -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 diff --git a/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb b/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb new file mode 100644 index 00000000..72c53a5a --- /dev/null +++ b/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb @@ -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 diff --git a/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb b/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb new file mode 100644 index 00000000..017357b3 --- /dev/null +++ b/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb @@ -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 diff --git a/lib/mutant/mutator/node/pattern_arguments.rb b/lib/mutant/mutator/node/pattern_arguments.rb new file mode 100644 index 00000000..42d68e55 --- /dev/null +++ b/lib/mutant/mutator/node/pattern_arguments.rb @@ -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 diff --git a/lib/mutant/mutator/node/pattern_variable.rb b/lib/mutant/mutator/node/pattern_variable.rb new file mode 100644 index 00000000..58c8e76d --- /dev/null +++ b/lib/mutant/mutator/node/pattern_variable.rb @@ -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 diff --git a/lib/mutant/mutator/node/send.rb b/lib/mutant/mutator/node/send.rb index 38a0867a..ea676953 100644 --- a/lib/mutant/mutator/node/send.rb +++ b/lib/mutant/mutator/node/send.rb @@ -26,7 +26,7 @@ module Mutant # # @return [undefined] # - # @api pirvate + # @api private # def emit_receiver unless self? diff --git a/lib/mutant/mutator/util.rb b/lib/mutant/mutator/util.rb index 685fb06d..4a929b23 100644 --- a/lib/mutant/mutator/util.rb +++ b/lib/mutant/mutator/util.rb @@ -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 diff --git a/lib/mutant/mutator/util/array.rb b/lib/mutant/mutator/util/array.rb new file mode 100644 index 00000000..4897d02f --- /dev/null +++ b/lib/mutant/mutator/util/array.rb @@ -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 diff --git a/lib/mutant/mutator/util/symbol.rb b/lib/mutant/mutator/util/symbol.rb new file mode 100644 index 00000000..1f10eba6 --- /dev/null +++ b/lib/mutant/mutator/util/symbol.rb @@ -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