Metric driven code refactoring

Break some classes, rename stuff etc.
This commit is contained in:
Markus Schirp 2013-01-04 22:16:03 +01:00
parent cca235d6e2
commit eb6ea9a74f
27 changed files with 291 additions and 229 deletions

View file

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

View file

@ -10,13 +10,14 @@ UncommunicativeParameterName:
LargeClass:
max_methods: 10
exclude:
#- "Mutant::Matcher::Method" # 13 methods
- "Mutant::Reporter::CLI" # 16 methods TODO Reduce!
- "Mutant::Reporter::Stats" # 6 ivars TODO Reduce!
- "Mutant::CLI" # 19 methods and 7 ivars, TODO Reduce!
enabled: true
max_instance_variables: 3
UncommunicativeMethodName:
accept: []
accept:
- sha1
exclude: []
enabled: true
reject:
@ -26,7 +27,9 @@ UncommunicativeMethodName:
LongParameterList:
max_params: 2
exclude:
- "Mutant::Mutator::Node#new_send_with_arguments" # 3 params
- "Mutant::Context::Constant#initialize" # 3 params
- "Mutant::Subject#initialize" # 3 params
enabled: true
overrides: {}
FeatureEnvy:
@ -43,6 +46,7 @@ IrresponsibleModule:
enabled: true
UncommunicativeModuleName:
accept:
- Mutant::Mutator::Node::Iter19
- Mutant::Strategy::Rspec::DM2
exclude: []
enabled: true
@ -53,6 +57,8 @@ NestedIterators:
ignore_iterators: []
exclude:
- Mutant#self.define_singleton_subclass
- Mutant::Mutator::Util::Array::Element#dispatch
- Mutant::Mutator::Node::ReceiverCase#emit_when_branch_mutation
enabled: true
max_allowed_nesting: 1
LongMethod:
@ -61,7 +67,8 @@ LongMethod:
enabled: true
Duplication:
allow_calls: []
exclude: []
exclude:
- Mutant::Mutator::Node::Literal::Hash#emit_element_presence
enabled: true
max_calls: 1
UtilityFunction:

View file

@ -63,6 +63,9 @@ require 'mutant/mutator/node/block'
require 'mutant/mutator/node/while'
require 'mutant/mutator/node/super'
require 'mutant/mutator/node/send'
require 'mutant/mutator/node/send/with_arguments'
require 'mutant/mutator/node/send/binary_operator_method'
require 'mutant/mutator/node/when'
require 'mutant/mutator/node/assignment'
require 'mutant/mutator/node/define'
require 'mutant/mutator/node/formal_arguments_19'
@ -75,7 +78,7 @@ require 'mutant/mutator/node/pattern_variable'
require 'mutant/mutator/node/default_arguments'
require 'mutant/mutator/node/return'
require 'mutant/mutator/node/iter_19'
require 'mutant/mutator/node/if_statement'
require 'mutant/mutator/node/if'
require 'mutant/mutator/node/receiver_case'
require 'mutant/loader'
require 'mutant/context'

View file

@ -17,15 +17,14 @@ module Mutant
# Initialize and insert mutation into vm
#
# @param [Rubinius::AST::Script] root
# @param [String] file
# @param [Fixnum] line
# @param [Subject] subject
#
# @return [undefined]
#
# @api private
#
def initialize(root, file, line)
@root, @file, @line = root, file, line
def initialize(root, subject)
@root, @subject = root, subject
run
end
@ -41,7 +40,7 @@ module Mutant
# @api private
#
def run
eval(source, TOPLEVEL_BINDING, @file, @line)
Kernel.eval(source, TOPLEVEL_BINDING, @subject.source_path, @subject.source_line)
end
# Return source

View file

@ -37,7 +37,7 @@ module Mutant
# @api private
#
def insert
Loader::Eval.run(root, subject.source_path, subject.source_line)
Loader::Eval.run(root, subject)
self
end
@ -110,9 +110,10 @@ module Mutant
@subject, @node = subject, node
end
# Noop mutation
class Noop < self
# Initialihe object
# Initialize object
#
# @param [Subject] subject
#

View file

@ -1,6 +1,8 @@
module Mutant
class Mutation
class Filter
# Whiltelist filter
class Whitelist < self
include Adamantium::Flat, Equalizer.new(:whitelist)

View file

@ -118,22 +118,31 @@ module Mutant
emit(new_nil)
end
# Return new Rubiinius::AST::SendWithArguments node
#
# @param [Rubnius::AST::Node] receiver
# @param [Symbol] name
# @param [Object] arguments
#
# @return [Rubinius::AST::SendWithArguments]
#
# @api private
#
def new_send_with_arguments(receiver, name, arguments)
new(Rubinius::AST::SendWithArguments, receiver, name, arguments)
end
# Return AST representing send
#
# @param [Rubinius::AST::Node] receiver
# @param [Symbol] name
# @param [Rubinius::AST::Node] arguments
#
# @return [Rubnius::AST::SendWithArguments]
# @return [Rubnius::AST::Send]
#
# @api private
#
def new_send(receiver, name, arguments=nil)
if arguments
new(Rubinius::AST::SendWithArguments, receiver, name, arguments)
else
new(Rubinius::AST::Send, receiver, name)
end
def new_send(receiver, name)
new(Rubinius::AST::Send, receiver, name)
end
# Return duplicated (unfrozen) node each call

View file

@ -1,10 +1,13 @@
module Mutant
class Mutator
class Node
# Abstract base class for assignment mutators
class Assignment < self
private
# Abstract base class for variable assignments
class Variable < self
# Emit mutants
@ -20,28 +23,31 @@ module Mutant
emit_attribute_mutations(:value)
end
# Mutator for local variable assignments
class Local < self
PREFIX = ''.freeze
handle(Rubinius::AST::LocalVariableAssignment)
end
# Mutator for instance variable assignments
class Instance < self
PREFIX = '@'.freeze
handle(Rubinius::AST::InstanceVariableAssignment)
end
# Mutator for class variable assignments
class Class < self
PREFIX = '@@'.freeze
handle(Rubinius::AST::ClassVariableAssignment)
end
# Mutator for global variable assignments
class Global < self
PREFIX = '$'.freeze
handle(Rubinius::AST::GlobalVariableAssignment)
end
end
end
end
end

View file

@ -2,7 +2,7 @@ module Mutant
class Mutator
class Node
# Mutator for Rubinius::AST::If nodes
class IfStatement < self
class If < self
handle(Rubinius::AST::If)
@ -16,13 +16,29 @@ module Mutant
#
def dispatch
emit_attribute_mutations(:condition)
emit_attribute_mutations(:body) if node.body.class != Rubinius::AST::NilLiteral
emit_attribute_mutations(:else) if node.else.class != Rubinius::AST::NilLiteral
emit_attribute_mutations(:body) unless nil_literal?(:body)
emit_attribute_mutations(:else) unless nil_literal?(:else)
emit_inverted_condition
emit_deleted_if_branch
emit_deleted_else_branch
end
# Test if attribute is non nil literal
#
# @param [Symbol] name
#
# @return [true]
# if attribute value a nil literal
#
# @return [false]
# otherwise
#
# @api private
#
def nil_literal?(name)
node.public_send(name).kind_of?(Rubinius::AST::NilLiteral)
end
# Emit inverted condition
#
# Especially the same like swap branches but more universal as it also
@ -53,9 +69,8 @@ module Mutant
# @api private
#
def emit_deleted_if_branch
body = else_branch
return unless body
emit_self(condition, else_branch, nil)
body = else_branch || return
emit_self(condition, body, nil)
end
# Return if_branch of node

View file

@ -40,7 +40,8 @@ module Mutant
# @api private
#
def nan
new_send(new_float(0), :/, new_float(0))
zero_float = new_float(0)
new_send_with_arguments(zero_float, :/, zero_float)
end
# Return AST representing negative infinity
@ -60,7 +61,7 @@ module Mutant
# @api private
#
def infinity
new_send(new_float(1), :/, new_float(0))
new_send_with_arguments(new_float(1), :/, new_float(0))
end
end
end

View file

@ -59,7 +59,8 @@ module Mutant
# @api private
#
def values
[[], [new_nil, new_nil] + dup_array]
nil_node = new_nil
[[], [nil_node, nil_node] + dup_array]
end
# Emit element presence mutations

View file

@ -27,7 +27,8 @@ module Mutant
# @api private
#
def inverse
new(inverse_class,node.start, node.finish)
node = self.node
new(inverse_class, node.start, node.finish)
end
# Emit range specific mutants

View file

@ -1,24 +1,6 @@
module Mutant
class Mutator
class Node
# Mutator for Rubinius::AST::When nodes
class When < self
handle(Rubinius::AST::When)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_attribute_mutations(:body)
end
end
# Mutator for Rubinius::AST::ReceiverCase nodes
class ReceiverCase < self
@ -57,7 +39,7 @@ module Mutant
# @api private
#
def emit_when_branch_mutations
when_branches.each_with_index do |branch,index|
when_branches.each_with_index do |branch, index|
Mutator.each(branch) do |mutant|
branches = dup_when_branches
branches[index]=mutant

View file

@ -1,17 +1,18 @@
module Mutant
class Mutator
class Node
# Class for mutations where messages are send to objects
# Namespace for send mutators
class Send < self
handle(Rubinius::AST::Send)
# Test if node corresponds to "self.class"
# Test if node name is a keyword
#
# @param [Rubinius::AST::Node] node
#
# @return [true]
# if node equals to self.class
# if node name equals a ruby keyword
#
# @return [false]
# otherwise
@ -19,9 +20,7 @@ module Mutant
# @api private
#
def self.keyword_name?(node)
node.kind_of?(Rubinius::AST::Send) &&
Mutant::KEYWORDS.include?(node.name) &&
node.receiver.kind_of?(Rubinius::AST::Self)
Mutant::KEYWORDS.include?(node.name)
end
private
@ -59,9 +58,7 @@ module Mutant
# @api private
#
def emit_block_mutations
if node.block
emit_attribute_mutations(:block)
end
emit_attribute_mutations(:block) if node.block
end
# Emit receiver mutations
@ -71,11 +68,7 @@ module Mutant
# @api private
#
def emit_receiver_mutations
util = self.class
unless to_self? or util.keyword_name?(receiver)
emit_attribute_mutations(:receiver)
end
emit_attribute_mutations(:receiver)
end
# Emit block absence mutation
@ -150,119 +143,14 @@ module Mutant
# @api private
#
def emit_implicit_self_receiver
return unless to_self?
return if self.class.keyword_name?(node)
if to_self? and self.class.keyword_name?(node)
return
end
mutant = dup_node
mutant.privately = true
# TODO: Fix rubinius to allow this as an attr_accessor
mutant.instance_variable_set(:@vcall_style, true)
emit(mutant)
end
class SendWithArguments < self
handle(Rubinius::AST::SendWithArguments)
class BinaryOperatorMethod < Node
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_left_mutations
emit_right_mutations
end
# Emit left mutations
#
# @return [undefined]
#
# @api private
#
def emit_left_mutations
emit_attribute_mutations(:receiver)
end
# Emit right mutations
#
# @return [undefined]
#
# @api private
#
def emit_right_mutations
right = node.arguments.array.first
Mutator.each(right).each do |mutated|
dup = dup_node
dup.arguments.array[0] = mutated
emit(dup)
end
end
end
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
super
emit_call_remove_mutation
emit_argument_mutations
end
# Test if message is a binary operator
#
# @return [true]
# if message is a binary operator
#
# @return [false]
# otherwise
#
# @api private
#
def binary_operator?
Mutant::BINARY_METHOD_OPERATORS.include?(node.name)
end
# Emit argument mutations
#
# @api private
#
# @return [undefined]
#
# @api private
#
def emit_argument_mutations
if binary_operator?
run(BinaryOperatorMethod)
return
end
emit_attribute_mutations(:arguments)
end
# Emit transfomr call mutation
#
# @return [undefined]
#
# @api private
#
def emit_call_remove_mutation
array = node.arguments.array
return unless array.length == 1
emit(array.first)
end
end
end
end
end

View file

@ -0,0 +1,51 @@
module Mutant
class Mutator
class Node
class Send
# Mutator for sends that correspond to a binary operator
class BinaryOperatorMethod < Node
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_left_mutations
emit_right_mutations
end
# Emit left mutations
#
# @return [undefined]
#
# @api private
#
def emit_left_mutations
emit_attribute_mutations(:receiver)
end
# Emit right mutations
#
# @return [undefined]
#
# @api private
#
def emit_right_mutations
right = node.arguments.array.first
Mutator.each(right).each do |mutated|
dup = dup_node
dup.arguments.array[0] = mutated
emit(dup)
end
end
end
end
end
end
end

View file

@ -0,0 +1,72 @@
module Mutant
class Mutator
class Node
class Send
# Mutator for send with arguments
class WithArguments < self
handle(Rubinius::AST::SendWithArguments)
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
super
emit_call_remove_mutation
emit_argument_mutations
end
# Test if message is a binary operator
#
# @return [true]
# if message is a binary operator
#
# @return [false]
# otherwise
#
# @api private
#
def binary_operator?
Mutant::BINARY_METHOD_OPERATORS.include?(node.name)
end
# Emit argument mutations
#
# @api private
#
# @return [undefined]
#
# @api private
#
def emit_argument_mutations
if binary_operator?
run(BinaryOperatorMethod)
return
end
emit_attribute_mutations(:arguments)
end
# Emit transfomr call mutation
#
# @return [undefined]
#
# @api private
#
def emit_call_remove_mutation
array = node.arguments.array
return unless array.length == 1
emit(array.first)
end
end
end
end
end
end

View file

@ -2,6 +2,7 @@ module Mutant
class Mutator
class Node
# Mutator for super without parantheses
class ZSuper < self
handle(Rubinius::AST::ZSuper)
@ -18,6 +19,7 @@ module Mutant
end
# Mutator for super with parantheses
class Super < self
handle(Rubinius::AST::Super)

View file

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

View file

@ -1,6 +1,8 @@
module Mutant
class Mutator
class Node
# Mutator for while expressions
class While < self
handle(Rubinius::AST::While)

View file

@ -7,6 +7,7 @@ module Mutant
handle(::Array)
# Element presence mutator
class Presence < Util
private
@ -27,6 +28,7 @@ module Mutant
end
# Array element mutator
class Element < Util
private
@ -40,7 +42,6 @@ module Mutant
def dispatch
input.each_with_index do |element, index|
dup = dup_input
Mutator.each(element).each do |mutation|
dup[index]=mutation
emit(dup)

View file

@ -1,6 +1,7 @@
module Mutant
class Reporter
# Null reporter
class Null < self
# Report subject

View file

@ -47,11 +47,10 @@ module Mutant
def initialize(config)
@config, @errors = config, []
reporter.config(config)
util_reporter = reporter
util_reporter.config(config)
run
reporter.errors(@errors)
util_reporter.errors(@errors)
end
# Return reporter

View file

@ -1,4 +1,6 @@
module Mutant
# Abstract base class for killing strategies
class Strategy
include AbstractType, Adamantium::Flat, Equalizer.new

View file

@ -116,10 +116,12 @@ module Mutant
# @api private
#
def glob_expression
base = base_path
if mutation.subject.matcher.public?
"#{base_path}/#{spec_file}"
"#{base}/#{spec_file}"
else
"#{base_path}/*_spec.rb"
"#{base}/*_spec.rb"
end
end

View file

@ -26,6 +26,39 @@ module Zombie
end
private_class_method :root
class DummySubject
# Return line
#
# @return [Fixnum]
#
# @api private
#
attr_reader :source_line
# Return path
#
# @return [String]
#
# @api private
#
attr_reader :source_path
private
# Initialize object
#
# @param [String] path
# @param [Fixnum] line
#
# @return [undefined]
#
# @api private
#
def initialize(path, line)
@source_path, @source_line = path, line
end
end
# Replace Mutant with Zombie namespace
#
@ -51,7 +84,7 @@ module Zombie
node.body = Rubinius::AST::ModuleScope.new(scope.line, node.name, scope.body)
end
::Mutant::Loader::Eval.run(root, path, 1)
::Mutant::Loader::Eval.run(root, DummySubject.new(path, 1))
end
private_class_method :zombify

View file

@ -2,15 +2,14 @@ require 'spec_helper'
describe Mutant::Loader::Eval, '.run' do
subject { object.run(node, file, line) }
subject { object.run(node, mutation_subject) }
let(:object) { described_class }
let(:file) { 'test.rb' }
let(:line) { 1 }
let(:object) { described_class }
let(:mutation_subject) { mock('Subject', :source_path => path, :source_line => line) }
let(:path) { 'test.rb' }
let(:line) { 1 }
let(:source) do
# This test case will blow up when not executed
# under toplevel binding.
<<-RUBY
class SomeNamespace
class Bar

View file

@ -1,42 +0,0 @@
require 'spec_helper'
describe Mutant::Loader::Rubinius, '.run' do
before do
pending
end
subject { object.run(node) }
let(:object) { described_class }
after do
Object.send(:remove_const, :SomeNamespace)
end
let(:source) do
# This test case will blow up when not executed
# under toplevel binding.
<<-RUBY
class SomeNamespace
class Bar
end
class SomeOther
class Foo < Bar
end
end
end
RUBY
end
let(:node) do
Rubinius::AST::Script.new(source.to_ast).tap do |source|
source.file = "/some/source"
end
end
it 'should load nodes into vm' do
subject
SomeNamespace::SomeOther::Foo
end
end