mutant 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -11
- data/.rspec +0 -1
- data/.travis.yml +14 -3
- data/Changelog.md +3 -0
- data/Gemfile +5 -1
- data/Gemfile.devtools +49 -0
- data/Guardfile +18 -0
- data/README.md +67 -0
- data/Rakefile +4 -1
- data/TODO +13 -0
- data/bin/mutant +14 -0
- data/bin/zombie +14 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/roodi.yml +26 -0
- data/config/site.reek +93 -0
- data/config/yardstick.yml +2 -0
- data/lib/inflector.rb +7 -0
- data/lib/inflector/defaults.rb +62 -0
- data/lib/inflector/inflections.rb +209 -0
- data/lib/inflector/methods.rb +149 -0
- data/lib/inflector/version.rb +3 -0
- data/lib/mutant.rb +96 -21
- data/lib/mutant/cli.rb +309 -0
- data/lib/mutant/color.rb +61 -0
- data/lib/mutant/context.rb +36 -0
- data/lib/mutant/context/scope.rb +138 -0
- data/lib/mutant/differ.rb +100 -0
- data/lib/mutant/helper.rb +38 -0
- data/lib/mutant/killer.rb +136 -0
- data/lib/mutant/killer/forking.rb +41 -0
- data/lib/mutant/killer/rspec.rb +49 -0
- data/lib/mutant/killer/static.rb +19 -0
- data/lib/mutant/loader.rb +129 -0
- data/lib/mutant/matcher.rb +55 -0
- data/lib/mutant/matcher/chain.rb +66 -0
- data/lib/mutant/matcher/method.rb +173 -0
- data/lib/mutant/matcher/method/classifier.rb +126 -0
- data/lib/mutant/matcher/method/instance.rb +67 -0
- data/lib/mutant/matcher/method/singleton.rb +141 -0
- data/lib/mutant/matcher/object_space.rb +114 -0
- data/lib/mutant/matcher/scope_methods.rb +127 -0
- data/lib/mutant/mutation.rb +101 -12
- data/lib/mutant/mutation/filter.rb +75 -0
- data/lib/mutant/mutation/filter/code.rb +68 -0
- data/lib/mutant/mutation/filter/regexp.rb +39 -0
- data/lib/mutant/mutation/filter/whitelist.rb +47 -0
- data/lib/mutant/mutator.rb +134 -30
- data/lib/mutant/mutator/node.rb +163 -0
- data/lib/mutant/mutator/node/arguments.rb +24 -0
- data/lib/mutant/mutator/node/block.rb +24 -0
- data/lib/mutant/mutator/node/define.rb +24 -0
- data/lib/mutant/mutator/node/if_statement.rb +93 -0
- data/lib/mutant/mutator/node/literal.rb +54 -0
- data/lib/mutant/mutator/node/literal/array.rb +28 -0
- data/lib/mutant/mutator/node/literal/boolean.rb +49 -0
- data/lib/mutant/mutator/node/literal/dynamic.rb +24 -0
- data/lib/mutant/mutator/node/literal/empty_array.rb +26 -0
- data/lib/mutant/mutator/node/literal/fixnum.rb +37 -0
- data/lib/mutant/mutator/node/literal/float.rb +48 -0
- data/lib/mutant/mutator/node/literal/hash.rb +89 -0
- data/lib/mutant/mutator/node/literal/nil.rb +25 -0
- data/lib/mutant/mutator/node/literal/range.rb +94 -0
- data/lib/mutant/mutator/node/literal/regex.rb +43 -0
- data/lib/mutant/mutator/node/literal/string.rb +26 -0
- data/lib/mutant/mutator/node/literal/symbol.rb +26 -0
- data/lib/mutant/mutator/node/noop.rb +55 -0
- data/lib/mutant/mutator/node/receiver_case.rb +140 -0
- data/lib/mutant/mutator/node/return.rb +31 -0
- data/lib/mutant/mutator/node/send.rb +112 -0
- data/lib/mutant/mutator/registry.rb +48 -0
- data/lib/mutant/mutator/util.rb +87 -0
- data/lib/mutant/random.rb +24 -27
- data/lib/mutant/reporter.rb +48 -30
- data/lib/mutant/reporter/cli.rb +221 -0
- data/lib/mutant/reporter/null.rb +42 -0
- data/lib/mutant/reporter/stats.rb +64 -0
- data/lib/mutant/runner.rb +112 -0
- data/lib/mutant/strategy.rb +42 -0
- data/lib/mutant/strategy/rspec.rb +59 -0
- data/lib/mutant/strategy/rspec/example_lookup.rb +122 -0
- data/lib/mutant/subject.rb +115 -0
- data/lib/mutant/support/method_object.rb +31 -0
- data/locator.rb +87 -0
- data/mutant.gemspec +21 -21
- data/spec/integration/mutant/differ_spec.rb +15 -0
- data/spec/integration/mutant/loader_spec.rb +21 -0
- data/spec/integration/mutant/method_matching_spec.rb +269 -0
- data/spec/integration/mutant/rspec_killer_spec.rb +24 -0
- data/spec/integration/mutant/runner_spec.rb +26 -0
- data/spec/integration/mutant/zombie_spec.rb +8 -0
- data/spec/rcov.opts +7 -0
- data/spec/shared/command_method_behavior.rb +7 -0
- data/spec/shared/each_method_behaviour.rb +15 -0
- data/spec/shared/hash_method_behavior.rb +17 -0
- data/spec/shared/idempotent_method_behavior.rb +7 -0
- data/spec/shared/invertible_method_behaviour.rb +9 -0
- data/spec/shared/method_filter_parse_behavior.rb +16 -0
- data/spec/shared/method_match_behavior.rb +39 -0
- data/spec/shared/mutator_behavior.rb +46 -0
- data/spec/spec_helper.rb +11 -14
- data/spec/support/compress_helper.rb +10 -0
- data/spec/support/rspec.rb +22 -0
- data/spec/support/test_app.rb +5 -0
- data/spec/support/zombie.rb +141 -0
- data/spec/unit/mutant/cli/class_methods/new_spec.rb +87 -0
- data/spec/unit/mutant/cli/class_methods/run_spec.rb +38 -0
- data/spec/unit/mutant/context/root_spec.rb +11 -0
- data/spec/unit/mutant/context/scope/class_methods/build_spec.rb +29 -0
- data/spec/unit/mutant/context/scope/root_spec.rb +22 -0
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +27 -0
- data/spec/unit/mutant/killer/fail_ques_spec.rb +39 -0
- data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +32 -0
- data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +33 -0
- data/spec/unit/mutant/loader/rubinius/class_methods/run_spec.rb +42 -0
- data/spec/unit/mutant/matcher/chain/each_spec.rb +37 -0
- data/spec/unit/mutant/matcher/chain/matchers_spec.rb +12 -0
- data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +49 -0
- data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +12 -0
- data/spec/unit/mutant/matcher/each_spec.rb +14 -0
- data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +21 -0
- data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +34 -0
- data/spec/unit/mutant/matcher/method/method_spec.rb +11 -0
- data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +24 -0
- data/spec/unit/mutant/matcher/object_space/each_spec.rb +31 -0
- data/spec/unit/mutant/mutator/each_spec.rb +25 -0
- data/spec/unit/mutant/mutator/emit_new_spec.rb +51 -0
- data/spec/unit/mutant/mutator/emit_spec.rb +52 -0
- data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +36 -0
- data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +47 -0
- data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +30 -0
- data/spec/unit/mutant/mutator/node/literal/array_spec.rb +30 -0
- data/spec/unit/mutant/mutator/node/literal/boolean/mutation_spec.rb +23 -0
- data/spec/unit/mutant/mutator/node/literal/empty_array_spec.rb +17 -0
- data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +17 -0
- data/spec/unit/mutant/mutator/node/literal/float_spec.rb +25 -0
- data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +34 -0
- data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +13 -0
- data/spec/unit/mutant/mutator/node/literal/range_spec.rb +35 -0
- data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +23 -0
- data/spec/unit/mutant/mutator/node/literal/string_spec.rb +17 -0
- data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +17 -0
- data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +27 -0
- data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +21 -0
- data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +78 -0
- data/spec/unit/mutant/mutator/self_spec.rb +7 -0
- data/spec/unit/mutant/subject/class_methods/new_spec.rb +13 -0
- data/spec/unit/mutant/subject/context_spec.rb +14 -0
- data/spec/unit/mutant/subject/each_spec.rb +35 -0
- data/spec/unit/mutant/subject/node_spec.rb +13 -0
- data/tasks/metrics/ci.rake +7 -0
- data/tasks/metrics/flay.rake +41 -0
- data/tasks/metrics/flog.rake +43 -0
- data/tasks/metrics/heckle.rake +216 -0
- data/tasks/metrics/metric_fu.rake +31 -0
- data/tasks/metrics/reek.rake +15 -0
- data/tasks/metrics/roodi.rake +15 -0
- data/tasks/metrics/yardstick.rake +23 -0
- data/tasks/spec.rake +45 -0
- data/tasks/yard.rake +9 -0
- data/test_app/.rspec +1 -0
- data/test_app/lib/test_app.rb +5 -0
- data/test_app/lib/test_app/literal.rb +32 -0
- data/test_app/spec/shared/command_method_behavior.rb +7 -0
- data/test_app/spec/shared/each_method_behaviour.rb +15 -0
- data/test_app/spec/shared/hash_method_behavior.rb +17 -0
- data/test_app/spec/shared/idempotent_method_behavior.rb +7 -0
- data/test_app/spec/shared/invertible_method_behaviour.rb +9 -0
- data/test_app/spec/shared/method_filter_parse_behavior.rb +16 -0
- data/test_app/spec/shared/method_match_behavior.rb +39 -0
- data/test_app/spec/shared/mutator_behavior.rb +44 -0
- data/test_app/spec/spec_helper.rb +7 -0
- data/test_app/spec/unit/test_app/literal/command_spec.rb +9 -0
- data/test_app/spec/unit/test_app/literal/string_spec.rb +9 -0
- metadata +346 -124
- data/.rvmrc +0 -1
- data/Readme.md +0 -13
- data/exe/mutate +0 -6
- data/lib/mutant/extensions.rb +0 -8
- data/lib/mutant/formatter.rb +0 -19
- data/lib/mutant/implementation.rb +0 -70
- data/lib/mutant/literal.rb +0 -147
- data/lib/mutant/method.rb +0 -31
- data/lib/mutant/mutatee.rb +0 -61
- data/lib/mutant/node.rb +0 -26
- data/lib/mutant/runners/rspec.rb +0 -34
- data/lib/mutant/version.rb +0 -3
- data/spec/functional/class_spec.rb +0 -46
- data/spec/functional/instance_method/array_spec.rb +0 -53
- data/spec/functional/instance_method/boolean_spec.rb +0 -101
- data/spec/functional/instance_method/call_spec.rb +0 -161
- data/spec/functional/instance_method/fixnum_spec.rb +0 -53
- data/spec/functional/instance_method/float_spec.rb +0 -53
- data/spec/functional/instance_method/hash_spec.rb +0 -53
- data/spec/functional/instance_method/if_spec.rb +0 -57
- data/spec/functional/instance_method/ivar_assign_spec.rb +0 -62
- data/spec/functional/instance_method/range_spec.rb +0 -53
- data/spec/functional/instance_method/regex_spec.rb +0 -55
- data/spec/functional/instance_method/string_spec.rb +0 -53
- data/spec/functional/instance_method/symbol_spec.rb +0 -53
- data/spec/functional/reporter/method_loaded_spec.rb +0 -62
- data/spec/functional/reporter/running_mutations_spec.rb +0 -60
- data/spec/functional/runners/rspec_spec.rb +0 -26
- data/spec/functional/singleton_method/array_spec.rb +0 -53
- data/spec/functional/singleton_method/boolean_spec.rb +0 -101
- data/spec/functional/singleton_method/call_spec.rb +0 -161
- data/spec/functional/singleton_method/fixnum_spec.rb +0 -53
- data/spec/functional/singleton_method/float_spec.rb +0 -53
- data/spec/functional/singleton_method/hash_spec.rb +0 -53
- data/spec/functional/singleton_method/if_spec.rb +0 -57
- data/spec/functional/singleton_method/ivar_assign_spec.rb +0 -60
- data/spec/functional/singleton_method/range_spec.rb +0 -53
- data/spec/functional/singleton_method/regex_spec.rb +0 -55
- data/spec/functional/singleton_method/string_spec.rb +0 -53
- data/spec/functional/singleton_method/symbol_spec.rb +0 -53
- data/spec/mutant/extensions_spec.rb +0 -13
- data/spec/mutant/implementation_spec.rb +0 -223
- data/spec/mutant/literal_spec.rb +0 -129
- data/spec/mutant/mutatee_spec.rb +0 -28
- data/spec/mutant/node_spec.rb +0 -41
- data/spec/mutant/random_spec.rb +0 -33
- data/spec/mutant/reporter_spec.rb +0 -17
- data/spec/mutant_spec.rb +0 -28
- data/spec/support/example_group_helpers.rb +0 -11
- data/spec/support/example_helpers.rb +0 -5
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Mutation
|
|
3
|
+
# Abstract filter for mutations
|
|
4
|
+
class Filter
|
|
5
|
+
include Adamantium::Flat, AbstractType
|
|
6
|
+
extend DescendantsTracker
|
|
7
|
+
|
|
8
|
+
# Check for match
|
|
9
|
+
#
|
|
10
|
+
# @param [Mutation] mutation
|
|
11
|
+
#
|
|
12
|
+
# @return [true]
|
|
13
|
+
# returns true if mutation is matched by filter
|
|
14
|
+
#
|
|
15
|
+
# @return [false]
|
|
16
|
+
# returns false otherwise
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
#
|
|
20
|
+
abstract_method :match?
|
|
21
|
+
|
|
22
|
+
# Build filter from string
|
|
23
|
+
#
|
|
24
|
+
# @param [String] notation
|
|
25
|
+
#
|
|
26
|
+
# @return [Filter]
|
|
27
|
+
# returns filter when can be buld from string
|
|
28
|
+
#
|
|
29
|
+
# @return [nil]
|
|
30
|
+
# returns nil otherwise
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
#
|
|
34
|
+
def self.build(notation)
|
|
35
|
+
descendants.each do |descendant|
|
|
36
|
+
filter = descendant.handle(notation)
|
|
37
|
+
return filter if filter
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return filter for handle
|
|
44
|
+
#
|
|
45
|
+
# @param [String] notation
|
|
46
|
+
#
|
|
47
|
+
# @return [nil]
|
|
48
|
+
# returns nil
|
|
49
|
+
#
|
|
50
|
+
# @api private
|
|
51
|
+
#
|
|
52
|
+
def self.handle(notation)
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Mutation filter matching all mutations
|
|
57
|
+
Mutant.define_singleton_subclass('ALL', self) do
|
|
58
|
+
|
|
59
|
+
# Test for match
|
|
60
|
+
#
|
|
61
|
+
# @pram [Mutation] mutation
|
|
62
|
+
#
|
|
63
|
+
# @return [true]
|
|
64
|
+
# returns true
|
|
65
|
+
#
|
|
66
|
+
# @api private
|
|
67
|
+
#
|
|
68
|
+
def match?(mutation)
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Mutation
|
|
3
|
+
class Filter
|
|
4
|
+
# Mutation filter that filters on mutation codes
|
|
5
|
+
class Code < self
|
|
6
|
+
include Equalizer.new(:code)
|
|
7
|
+
|
|
8
|
+
# Test for match
|
|
9
|
+
#
|
|
10
|
+
# @param [Mutation] mutation
|
|
11
|
+
#
|
|
12
|
+
# @return [true]
|
|
13
|
+
# returns true if mutation code matches filter code
|
|
14
|
+
#
|
|
15
|
+
# @return [false]
|
|
16
|
+
# returns false otherwise
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
#
|
|
20
|
+
def match?(mutation)
|
|
21
|
+
mutation.code.eql?(code)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
PATTERN = %r(\Acode:([a-f0-9]{1,6})\z).freeze
|
|
25
|
+
|
|
26
|
+
# Test if class handles string
|
|
27
|
+
#
|
|
28
|
+
# @param [String] notation
|
|
29
|
+
#
|
|
30
|
+
# @return [Filter]
|
|
31
|
+
# return code filter instance if notation matches pattern
|
|
32
|
+
#
|
|
33
|
+
# @return [nil]
|
|
34
|
+
# returns nil otherwise
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
37
|
+
#
|
|
38
|
+
def self.handle(notation)
|
|
39
|
+
match = PATTERN.match(notation)
|
|
40
|
+
return unless match
|
|
41
|
+
new(match[1])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Return code
|
|
45
|
+
#
|
|
46
|
+
# @return [String]
|
|
47
|
+
#
|
|
48
|
+
# @api private
|
|
49
|
+
#
|
|
50
|
+
attr_reader :code
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Initialize code filter
|
|
55
|
+
#
|
|
56
|
+
# @param [String] code
|
|
57
|
+
#
|
|
58
|
+
# @return [undefined]
|
|
59
|
+
#
|
|
60
|
+
# @api private
|
|
61
|
+
#
|
|
62
|
+
def initialize(code)
|
|
63
|
+
@code = code
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Mutation
|
|
3
|
+
class Filter
|
|
4
|
+
# Mutaiton filter filtering in regexp match on mutation identification
|
|
5
|
+
class Regexp < self
|
|
6
|
+
|
|
7
|
+
# Test for match
|
|
8
|
+
#
|
|
9
|
+
# @param [Mutation] mutation
|
|
10
|
+
#
|
|
11
|
+
# @return [true]
|
|
12
|
+
# returns true if mutation identification is matched by regexp
|
|
13
|
+
#
|
|
14
|
+
# @return [false]
|
|
15
|
+
# returns false otherwise
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
#
|
|
19
|
+
def match?(mutation)
|
|
20
|
+
!!@regexp.match =~ mutation.identification
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Initialize regexp filter
|
|
26
|
+
#
|
|
27
|
+
# @param [Regexp] regexp
|
|
28
|
+
#
|
|
29
|
+
# @return [undefined]
|
|
30
|
+
#
|
|
31
|
+
# @api private
|
|
32
|
+
#
|
|
33
|
+
def initialize(regexp)
|
|
34
|
+
@regexp = regexp
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Mutation
|
|
3
|
+
class Filter
|
|
4
|
+
class Whitelist < self
|
|
5
|
+
include Adamantium::Flat, Equalizer.new(:whitelist)
|
|
6
|
+
|
|
7
|
+
# Test for match
|
|
8
|
+
#
|
|
9
|
+
# @param [Mutation]
|
|
10
|
+
#
|
|
11
|
+
# @return [true]
|
|
12
|
+
# returns true if mutation matches whitelist
|
|
13
|
+
#
|
|
14
|
+
# @return [false]
|
|
15
|
+
# returns false otherwise
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
#
|
|
19
|
+
def match?(mutation)
|
|
20
|
+
@whitelist.any? { |filter| filter.match?(mutation) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Return whitelist
|
|
24
|
+
#
|
|
25
|
+
# @return [Enumerable<Filter>]
|
|
26
|
+
#
|
|
27
|
+
# @api private
|
|
28
|
+
#
|
|
29
|
+
attr_reader :whitelist
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Initalize white list
|
|
34
|
+
#
|
|
35
|
+
# @param [Enumerable<Filter>] whitelist
|
|
36
|
+
#
|
|
37
|
+
# @return [undefined]
|
|
38
|
+
#
|
|
39
|
+
# @api private
|
|
40
|
+
#
|
|
41
|
+
def initialize(whitelist)
|
|
42
|
+
@whitelist = whitelist
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/mutant/mutator.rb
CHANGED
|
@@ -1,49 +1,153 @@
|
|
|
1
1
|
module Mutant
|
|
2
|
+
# Generator for mutations
|
|
2
3
|
class Mutator
|
|
3
|
-
|
|
4
|
+
include Adamantium::Flat, AbstractType
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
# Run mutator on input
|
|
7
|
+
#
|
|
8
|
+
# @param [Object] input
|
|
9
|
+
# @param [#call] block
|
|
10
|
+
#
|
|
11
|
+
# @return [self]
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
#
|
|
15
|
+
def self.each(node, &block)
|
|
16
|
+
return to_enum(__method__, node) unless block_given?
|
|
17
|
+
Registry.lookup(node.class).new(node, block)
|
|
18
|
+
|
|
19
|
+
self
|
|
7
20
|
end
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
# Register node class handler
|
|
23
|
+
#
|
|
24
|
+
# @param [Class:Rubinius::AST::Node] node_class
|
|
25
|
+
#
|
|
26
|
+
# @return [undefined]
|
|
27
|
+
#
|
|
28
|
+
# @api private
|
|
29
|
+
#
|
|
30
|
+
def self.handle(node_class)
|
|
31
|
+
Registry.register(node_class,self)
|
|
32
|
+
end
|
|
33
|
+
private_class_method :handle
|
|
12
34
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
# Return input
|
|
36
|
+
#
|
|
37
|
+
# @return [Object]
|
|
38
|
+
#
|
|
39
|
+
# @api private
|
|
40
|
+
#
|
|
41
|
+
attr_reader :input
|
|
19
42
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Initialize object
|
|
46
|
+
#
|
|
47
|
+
# @param [Object] input
|
|
48
|
+
# @param [#call(node)] block
|
|
49
|
+
#
|
|
50
|
+
# @return [undefined]
|
|
51
|
+
#
|
|
52
|
+
# @api private
|
|
53
|
+
#
|
|
54
|
+
def initialize(input, block)
|
|
55
|
+
@input, @block = Helper.deep_clone(input), block
|
|
56
|
+
IceNine.deep_freeze(@input)
|
|
57
|
+
dispatch
|
|
29
58
|
end
|
|
30
59
|
|
|
31
|
-
|
|
32
|
-
|
|
60
|
+
# Test if generated object is different from input
|
|
61
|
+
#
|
|
62
|
+
# @param [Object] object
|
|
63
|
+
#
|
|
64
|
+
# @return [true]
|
|
65
|
+
# if generated object is different
|
|
66
|
+
#
|
|
67
|
+
# @return [false]
|
|
68
|
+
#
|
|
69
|
+
# @api private
|
|
70
|
+
#
|
|
71
|
+
def new?(object)
|
|
72
|
+
input != object
|
|
33
73
|
end
|
|
34
74
|
|
|
35
|
-
|
|
36
|
-
|
|
75
|
+
# Dispatch node generations
|
|
76
|
+
#
|
|
77
|
+
# @return [undefined]
|
|
78
|
+
#
|
|
79
|
+
# @api private
|
|
80
|
+
#
|
|
81
|
+
abstract_method :dispatch
|
|
82
|
+
|
|
83
|
+
# Emit generated mutation if object is not equivalent to input
|
|
84
|
+
#
|
|
85
|
+
# @param [Object] object
|
|
86
|
+
#
|
|
87
|
+
# @return [undefined]
|
|
88
|
+
#
|
|
89
|
+
# @api private
|
|
90
|
+
#
|
|
91
|
+
def emit(object)
|
|
92
|
+
return unless new?(object)
|
|
93
|
+
|
|
94
|
+
emit!(object)
|
|
37
95
|
end
|
|
38
96
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
97
|
+
# Maximum amount of tries to generate a new object
|
|
98
|
+
MAX_TRIES = 3
|
|
99
|
+
|
|
100
|
+
# Call block until it generates a mutation
|
|
101
|
+
#
|
|
102
|
+
# The primary use of this method is to give the random generated object
|
|
103
|
+
# a nice interface for retring generation when generation accidentally generated the
|
|
104
|
+
# input
|
|
105
|
+
#
|
|
106
|
+
# @yield
|
|
107
|
+
# Execute block until object is generated where new?(object) returns true
|
|
108
|
+
#
|
|
109
|
+
# @return [self]
|
|
110
|
+
#
|
|
111
|
+
# @raise [RuntimeError]
|
|
112
|
+
# raises RuntimeError in case no new ast node can be generated after MAX_TRIES.
|
|
113
|
+
#
|
|
114
|
+
# @api private
|
|
115
|
+
#
|
|
116
|
+
def emit_new
|
|
117
|
+
MAX_TRIES.times do
|
|
118
|
+
object = yield
|
|
119
|
+
|
|
120
|
+
if new?(object)
|
|
121
|
+
emit!(object)
|
|
122
|
+
return
|
|
123
|
+
end
|
|
42
124
|
end
|
|
125
|
+
|
|
126
|
+
raise "New AST could not be generated after #{MAX_TRIES} attempts"
|
|
43
127
|
end
|
|
44
128
|
|
|
45
|
-
|
|
46
|
-
|
|
129
|
+
# Call block with node
|
|
130
|
+
#
|
|
131
|
+
# @param [Rubinius::AST::Node] node
|
|
132
|
+
#
|
|
133
|
+
# @return [self]
|
|
134
|
+
#
|
|
135
|
+
# @api private
|
|
136
|
+
#
|
|
137
|
+
def emit!(node)
|
|
138
|
+
@block.call(node)
|
|
139
|
+
self
|
|
47
140
|
end
|
|
141
|
+
|
|
142
|
+
# Shortcut to create a new unfrozen duplicate of input
|
|
143
|
+
#
|
|
144
|
+
# @return [Object]
|
|
145
|
+
#
|
|
146
|
+
# @api private
|
|
147
|
+
#
|
|
148
|
+
def dup_input
|
|
149
|
+
input.dup
|
|
150
|
+
end
|
|
151
|
+
|
|
48
152
|
end
|
|
49
153
|
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
# Generator for mutations
|
|
3
|
+
class Mutator
|
|
4
|
+
# Abstract base class for node mutators
|
|
5
|
+
class Node < self
|
|
6
|
+
include AbstractType
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
alias_method :node, :input
|
|
11
|
+
alias_method :dup_node, :dup_input
|
|
12
|
+
|
|
13
|
+
# Return source of input node
|
|
14
|
+
#
|
|
15
|
+
# @return [String]
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
#
|
|
19
|
+
def source
|
|
20
|
+
ToSource.to_source(node)
|
|
21
|
+
end
|
|
22
|
+
memoize :source
|
|
23
|
+
|
|
24
|
+
# Test if generated node is new
|
|
25
|
+
#
|
|
26
|
+
# @return [true]
|
|
27
|
+
# if generated node is different from input
|
|
28
|
+
#
|
|
29
|
+
# @return [false]
|
|
30
|
+
# otherwise
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
#
|
|
34
|
+
def new?(node)
|
|
35
|
+
source != ToSource.to_source(node)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Emit a new AST node
|
|
39
|
+
#
|
|
40
|
+
# @param [Rubinis::AST::Node:Class] node_class
|
|
41
|
+
#
|
|
42
|
+
# @return [Rubinius::AST::Node]
|
|
43
|
+
#
|
|
44
|
+
# @api private
|
|
45
|
+
#
|
|
46
|
+
def emit_node(node_class, *arguments)
|
|
47
|
+
emit(new(node_class, *arguments))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Create a new AST node with same class as wrapped node
|
|
51
|
+
#
|
|
52
|
+
# @return [Rubinius::AST::Node]
|
|
53
|
+
#
|
|
54
|
+
# @api private
|
|
55
|
+
#
|
|
56
|
+
def new_self(*arguments)
|
|
57
|
+
new(node.class, *arguments)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Create a new AST node with Rubnius::AST::NilLiteral class
|
|
61
|
+
#
|
|
62
|
+
# @return [Rubinius::AST::Node]
|
|
63
|
+
#
|
|
64
|
+
# @api private
|
|
65
|
+
#
|
|
66
|
+
def new_nil
|
|
67
|
+
new(Rubinius::AST::NilLiteral)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Create a new AST node with the same line as wrapped node
|
|
71
|
+
#
|
|
72
|
+
# @param [Class:Rubinius::AST::Node] node_class
|
|
73
|
+
#
|
|
74
|
+
# @return [Rubinius::AST::Node]
|
|
75
|
+
#
|
|
76
|
+
# @api private
|
|
77
|
+
#
|
|
78
|
+
def new(node_class, *arguments)
|
|
79
|
+
node_class.new(node.line, *arguments)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Emit a new AST node with same class as wrapped node
|
|
83
|
+
#
|
|
84
|
+
# @return [undefined]
|
|
85
|
+
#
|
|
86
|
+
# @api private
|
|
87
|
+
#
|
|
88
|
+
def emit_self(*arguments)
|
|
89
|
+
emit(new_self(*arguments))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Emit a new node with wrapping class for each entry in values
|
|
93
|
+
#
|
|
94
|
+
# @param [Array] values
|
|
95
|
+
#
|
|
96
|
+
# @return [undefined]
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
#
|
|
100
|
+
def emit_values(values)
|
|
101
|
+
values.each do |value|
|
|
102
|
+
emit_self(value)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Emit body mutations
|
|
107
|
+
#
|
|
108
|
+
# @param [Symbol] name
|
|
109
|
+
#
|
|
110
|
+
# @return [undefined]
|
|
111
|
+
#
|
|
112
|
+
# @api private
|
|
113
|
+
#
|
|
114
|
+
def emit_attribute_mutations(name)
|
|
115
|
+
body = node.public_send(name)
|
|
116
|
+
|
|
117
|
+
Mutator.each(body) do |mutation|
|
|
118
|
+
dup = dup_node
|
|
119
|
+
dup.public_send(:"#{name}=", mutation)
|
|
120
|
+
emit(dup)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Emit a new AST node with NilLiteral class
|
|
125
|
+
#
|
|
126
|
+
# @return [Rubinius::AST::NilLiteral]
|
|
127
|
+
#
|
|
128
|
+
# @api private
|
|
129
|
+
#
|
|
130
|
+
def emit_nil
|
|
131
|
+
emit(new_nil)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Return AST representing send
|
|
135
|
+
#
|
|
136
|
+
# @param [Rubinius::AST::Node] receiver
|
|
137
|
+
# @param [Symbol] name
|
|
138
|
+
# @param [Rubinius::AST::Node] arguments
|
|
139
|
+
#
|
|
140
|
+
# @return [Rubnius::AST::SendWithArguments]
|
|
141
|
+
#
|
|
142
|
+
# @api private
|
|
143
|
+
#
|
|
144
|
+
def new_send(receiver, name, arguments=nil)
|
|
145
|
+
if arguments
|
|
146
|
+
new(Rubinius::AST::SendWithArguments, receiver, name, arguments)
|
|
147
|
+
else
|
|
148
|
+
new(Rubinius::AST::Send, receiver, name)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Return duplicated (unfrozen) node each call
|
|
153
|
+
#
|
|
154
|
+
# @return [Rubinius::AST::Node]
|
|
155
|
+
#
|
|
156
|
+
# @api private
|
|
157
|
+
#
|
|
158
|
+
def dup_node
|
|
159
|
+
node.dup
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|