mutant 0.2.20 → 0.3.0.beta2
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.
- checksums.yaml +7 -0
- data/.travis.yml +10 -11
- data/Changelog.md +93 -38
- data/Gemfile +3 -1
- data/Gemfile.devtools +16 -20
- data/Guardfile +1 -1
- data/README.md +36 -16
- data/Rakefile +21 -2
- data/TODO +11 -7
- data/bin/mutant +4 -0
- data/bin/zombie +4 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/{site.reek → reek.yml} +94 -70
- data/lib/mutant/cli/classifier/method.rb +100 -0
- data/lib/mutant/cli/classifier/namespace.rb +47 -0
- data/lib/mutant/cli/classifier/scope.rb +35 -0
- data/lib/mutant/cli/classifier.rb +141 -0
- data/lib/mutant/cli.rb +115 -162
- data/lib/mutant/color.rb +2 -2
- data/lib/mutant/config.rb +27 -0
- data/lib/mutant/constants.rb +32 -17
- data/lib/mutant/context/scope.rb +33 -51
- data/lib/mutant/context.rb +8 -19
- data/lib/mutant/differ.rb +5 -5
- data/lib/mutant/helper.rb +2 -17
- data/lib/mutant/killer/forked.rb +44 -0
- data/lib/mutant/killer/forking.rb +3 -57
- data/lib/mutant/killer/rspec.rb +16 -20
- data/lib/mutant/killer/static.rb +6 -7
- data/lib/mutant/killer.rb +48 -74
- data/lib/mutant/loader.rb +6 -6
- data/lib/mutant/matcher/chain.rb +4 -25
- data/lib/mutant/matcher/method/instance.rb +14 -24
- data/lib/mutant/matcher/method/singleton.rb +35 -46
- data/lib/mutant/matcher/method.rb +95 -83
- data/lib/mutant/matcher/{scope_methods.rb → methods.rb} +53 -76
- data/lib/mutant/matcher/namespace.rb +71 -0
- data/lib/mutant/matcher/scope.rb +34 -0
- data/lib/mutant/matcher.rb +24 -34
- data/lib/mutant/mutation/evil.rb +35 -0
- data/lib/mutant/mutation/filter/code.rb +7 -28
- data/lib/mutant/mutation/filter/regexp.rb +6 -18
- data/lib/mutant/mutation/filter/whitelist.rb +5 -4
- data/lib/mutant/mutation/filter.rb +10 -9
- data/lib/mutant/mutation/neutral.rb +35 -0
- data/lib/mutant/mutation.rb +21 -61
- data/lib/mutant/mutator/node/argument.rb +88 -0
- data/lib/mutant/mutator/node/arguments.rb +52 -0
- data/lib/mutant/mutator/node/assignment.rb +34 -38
- data/lib/mutant/mutator/node/begin.rb +33 -0
- data/lib/mutant/mutator/node/block.rb +14 -14
- data/lib/mutant/mutator/node/case.rb +59 -0
- data/lib/mutant/mutator/node/define.rb +26 -22
- data/lib/mutant/mutator/node/if.rb +31 -71
- data/lib/mutant/mutator/node/literal/array.rb +25 -9
- data/lib/mutant/mutator/node/literal/boolean.rb +13 -30
- data/lib/mutant/mutator/node/literal/dynamic.rb +6 -5
- data/lib/mutant/mutator/node/literal/fixnum.rb +18 -7
- data/lib/mutant/mutator/node/literal/float.rb +15 -8
- data/lib/mutant/mutator/node/literal/hash.rb +33 -52
- data/lib/mutant/mutator/node/literal/nil.rb +8 -7
- data/lib/mutant/mutator/node/literal/range.rb +25 -50
- data/lib/mutant/mutator/node/literal/regex.rb +15 -23
- data/lib/mutant/mutator/node/literal/string.rb +7 -6
- data/lib/mutant/mutator/node/literal/symbol.rb +7 -6
- data/lib/mutant/mutator/node/literal.rb +4 -46
- data/lib/mutant/mutator/node/mlhs.rb +27 -0
- data/lib/mutant/mutator/node/noop.rb +18 -43
- data/lib/mutant/mutator/node/return.rb +8 -8
- data/lib/mutant/mutator/node/send/binary.rb +31 -0
- data/lib/mutant/mutator/node/send.rb +106 -72
- data/lib/mutant/mutator/node/super.rb +15 -20
- data/lib/mutant/mutator/node/when.rb +32 -7
- data/lib/mutant/mutator/node/while.rb +9 -7
- data/lib/mutant/mutator/node.rb +116 -66
- data/lib/mutant/mutator/registry.rb +14 -11
- data/lib/mutant/mutator/util/array.rb +9 -9
- data/lib/mutant/mutator/util/symbol.rb +6 -20
- data/lib/mutant/mutator/util.rb +6 -3
- data/lib/mutant/mutator.rb +12 -28
- data/lib/mutant/node_helpers.rb +28 -0
- data/lib/mutant/random.rb +3 -2
- data/lib/mutant/reporter/cli/printer/config.rb +174 -0
- data/lib/mutant/reporter/cli/printer/killer.rb +42 -0
- data/lib/mutant/reporter/cli/printer/mutation.rb +55 -0
- data/lib/mutant/reporter/cli/printer/subject.rb +147 -0
- data/lib/mutant/reporter/cli/printer.rb +165 -0
- data/lib/mutant/reporter/cli.rb +9 -277
- data/lib/mutant/reporter/null.rb +6 -30
- data/lib/mutant/reporter.rb +6 -73
- data/lib/mutant/runner/config.rb +82 -0
- data/lib/mutant/runner/mutation.rb +58 -0
- data/lib/mutant/runner/subject.rb +81 -0
- data/lib/mutant/runner.rb +42 -92
- data/lib/mutant/singleton_methods.rb +2 -2
- data/lib/mutant/strategy/method_expansion.rb +51 -0
- data/lib/mutant/strategy/rspec/dm2/lookup/method.rb +142 -0
- data/lib/mutant/strategy/rspec/dm2/lookup.rb +61 -0
- data/lib/mutant/strategy/rspec/dm2.rb +22 -0
- data/lib/mutant/strategy/rspec.rb +20 -22
- data/lib/mutant/strategy/static.rb +18 -0
- data/lib/mutant/strategy.rb +15 -50
- data/lib/mutant/subject/method.rb +100 -0
- data/lib/mutant/subject.rb +18 -49
- data/lib/mutant/support/method_object.rb +4 -2
- data/lib/mutant.rb +40 -35
- data/mutant.gemspec +9 -8
- data/spec/integration/mutant/rspec_killer_spec.rb +3 -3
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +9 -0
- data/spec/integration/mutant/zombie_spec.rb +1 -1
- data/spec/shared/method_matcher_behavior.rb +35 -0
- data/spec/shared/mutator_behavior.rb +63 -32
- data/spec/spec_helper.rb +13 -3
- data/spec/support/ice_nine_config.rb +8 -0
- data/spec/support/rspec.rb +1 -1
- data/spec/support/zombie.rb +1 -1
- data/spec/unit/mutant/cli/class_methods/new_spec.rb +42 -28
- data/spec/unit/mutant/cli/class_methods/run_spec.rb +15 -13
- data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +44 -0
- data/spec/unit/mutant/context/scope/root_spec.rb +4 -4
- data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +6 -5
- data/spec/unit/mutant/killer/success_predicate_spec.rb +28 -0
- data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +1 -1
- data/spec/unit/mutant/matcher/chain/each_spec.rb +1 -1
- data/spec/unit/mutant/matcher/chain/matchers_spec.rb +1 -1
- data/spec/unit/mutant/matcher/method/instance/each_spec.rb +112 -0
- data/spec/unit/mutant/matcher/method/singleton/each_spec.rb +93 -0
- data/spec/unit/mutant/matcher/methods/instance/each_spec.rb +59 -0
- data/spec/unit/mutant/matcher/methods/singleton/each_spec.rb +53 -0
- data/spec/unit/mutant/matcher/namespace/each_spec.rb +37 -0
- data/spec/unit/mutant/mutator/node/begin/mutation_spec.rb +33 -0
- data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +42 -14
- data/spec/unit/mutant/mutator/node/case/mutation_spec.rb +319 -0
- data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +31 -27
- data/spec/unit/mutant/mutator/node/if/mutation_spec.rb +75 -0
- data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +2 -2
- data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +1 -3
- data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +1 -9
- data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +6 -2
- data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +111 -108
- data/spec/unit/mutant/mutator/node/super/mutation_spec.rb +0 -33
- data/spec/unit/mutant/mutator/node/while/mutation_spec.rb +2 -2
- data/spec/unit/mutant/runner/config/subjects_spec.rb +38 -0
- data/spec/unit/mutant/runner/config/success_predicate_spec.rb +53 -0
- data/spec/unit/mutant/runner/failed_predicte_spec.rb +33 -0
- data/spec/unit/mutant/runner/mutation/killer_spec.rb +39 -0
- data/spec/unit/mutant/runner/subject/success_predicate_spec.rb +49 -0
- data/spec/unit/mutant/strategy/method_expansion/class_methods/run_spec.rb +49 -0
- data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +52 -0
- data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +42 -0
- data/spec/unit/mutant/subject/context_spec.rb +6 -3
- data/spec/unit/mutant/subject/each_spec.rb +11 -8
- data/spec/unit/mutant/subject/node_spec.rb +6 -2
- data/test_app/spec/shared/method_filter_parse_behavior.rb +0 -2
- data/test_app/spec/shared/method_match_behavior.rb +1 -1
- data/test_app/spec/spec_helper.rb +4 -2
- metadata +101 -109
- data/config/roodi.yml +0 -26
- data/lib/mutant/matcher/method/classifier.rb +0 -141
- data/lib/mutant/matcher/object_space.rb +0 -114
- data/lib/mutant/mutator/node/actual_arguments.rb +0 -25
- data/lib/mutant/mutator/node/default_arguments.rb +0 -25
- data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +0 -33
- data/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb +0 -35
- data/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb +0 -37
- data/lib/mutant/mutator/node/formal_arguments_19.rb +0 -41
- data/lib/mutant/mutator/node/iter_19.rb +0 -27
- data/lib/mutant/mutator/node/literal/empty_array.rb +0 -26
- data/lib/mutant/mutator/node/pattern_arguments.rb +0 -41
- data/lib/mutant/mutator/node/pattern_variable.rb +0 -23
- data/lib/mutant/mutator/node/receiver_case.rb +0 -122
- data/lib/mutant/mutator/node/send/binary_operator_method.rb +0 -61
- data/lib/mutant/mutator/node/send/with_arguments.rb +0 -81
- data/lib/mutant/reporter/stats.rb +0 -120
- data/lib/mutant/strategy/rspec/example_lookup.rb +0 -163
- data/spec/integration/mutant/method_matching_spec.rb +0 -269
- data/spec/shared/method_match_behavior.rb +0 -39
- data/spec/unit/mutant/killer/fail_ques_spec.rb +0 -39
- data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +0 -49
- data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +0 -12
- data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +0 -21
- data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +0 -52
- data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +0 -24
- data/spec/unit/mutant/matcher/object_space/each_spec.rb +0 -31
- data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +0 -60
- data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +0 -27
- data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +0 -236
- data/spec/unit/mutant/subject/class_methods/new_spec.rb +0 -13
- data/tasks/metrics/ci.rake +0 -7
- data/tasks/metrics/flay.rake +0 -41
- data/tasks/metrics/flog.rake +0 -43
- data/tasks/metrics/heckle.rake +0 -216
- data/tasks/metrics/metric_fu.rake +0 -31
- data/tasks/metrics/reek.rake +0 -15
- data/tasks/metrics/roodi.rake +0 -15
- data/tasks/metrics/yardstick.rake +0 -23
- data/tasks/spec.rake +0 -45
- data/tasks/yard.rake +0 -9
|
@@ -3,6 +3,7 @@ module Mutant
|
|
|
3
3
|
class Method
|
|
4
4
|
# Matcher for singleton methods
|
|
5
5
|
class Singleton < self
|
|
6
|
+
SUBJECT_CLASS = Subject::Method::Singleton
|
|
6
7
|
|
|
7
8
|
# Return identification
|
|
8
9
|
#
|
|
@@ -15,117 +16,105 @@ module Mutant
|
|
|
15
16
|
end
|
|
16
17
|
memoize :identification
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# if method is public
|
|
22
|
-
#
|
|
23
|
-
# @return [false]
|
|
24
|
-
# otherwise
|
|
25
|
-
#
|
|
26
|
-
# @api private
|
|
27
|
-
#
|
|
28
|
-
def public?
|
|
29
|
-
scope.singleton_class.public_method_defined?(method_name)
|
|
30
|
-
end
|
|
31
|
-
memoize :public?
|
|
32
|
-
|
|
19
|
+
RECEIVER_INDEX = 0
|
|
20
|
+
NAME_INDEX = 1
|
|
21
|
+
CONST_NAME_INDEX = 1
|
|
33
22
|
|
|
34
23
|
private
|
|
35
24
|
|
|
36
25
|
# Test for node match
|
|
37
26
|
#
|
|
38
|
-
# @param [
|
|
27
|
+
# @param [Parser::AST::Node] node
|
|
39
28
|
#
|
|
40
29
|
# @return [true]
|
|
41
|
-
#
|
|
30
|
+
# if node matches method
|
|
42
31
|
#
|
|
43
32
|
# @return [false]
|
|
44
|
-
#
|
|
33
|
+
# otherwise
|
|
45
34
|
#
|
|
46
35
|
# @api private
|
|
47
36
|
#
|
|
48
37
|
def match?(node)
|
|
49
|
-
node
|
|
50
|
-
line?(node) &&
|
|
51
|
-
name?(node) &&
|
|
52
|
-
receiver?(node)
|
|
38
|
+
line?(node) && name?(node) && receiver?(node)
|
|
53
39
|
end
|
|
54
40
|
|
|
55
41
|
# Test for line match
|
|
56
42
|
#
|
|
57
|
-
# @param [
|
|
43
|
+
# @param [Parser::AST::Node] node
|
|
58
44
|
#
|
|
59
45
|
# @return [true]
|
|
60
|
-
#
|
|
46
|
+
# if node matches source line
|
|
61
47
|
#
|
|
62
48
|
# @return [false]
|
|
63
|
-
#
|
|
49
|
+
# otherwise
|
|
64
50
|
#
|
|
65
51
|
# @api private
|
|
66
52
|
#
|
|
67
53
|
def line?(node)
|
|
68
|
-
node.
|
|
54
|
+
expression = node.location.expression
|
|
55
|
+
return false unless expression
|
|
56
|
+
expression.line == source_line
|
|
69
57
|
end
|
|
70
58
|
|
|
71
59
|
# Test for name match
|
|
72
60
|
#
|
|
73
|
-
# @param [
|
|
61
|
+
# @param [Parser::AST::Node] node
|
|
74
62
|
#
|
|
75
63
|
# @return [true]
|
|
76
|
-
#
|
|
64
|
+
# if node name matches
|
|
77
65
|
#
|
|
78
66
|
# @return [false]
|
|
79
|
-
#
|
|
67
|
+
# otherwise
|
|
80
68
|
#
|
|
81
69
|
# @api private
|
|
82
70
|
#
|
|
83
71
|
def name?(node)
|
|
84
|
-
node.
|
|
72
|
+
node.children[NAME_INDEX] == method_name
|
|
85
73
|
end
|
|
86
74
|
|
|
87
75
|
# Test for receiver match
|
|
88
76
|
#
|
|
89
|
-
# @param [
|
|
77
|
+
# @param [Parser::AST::Node] node
|
|
90
78
|
#
|
|
91
79
|
# @return [true]
|
|
92
|
-
#
|
|
80
|
+
# when receiver matches
|
|
93
81
|
#
|
|
94
82
|
# @return [false]
|
|
95
|
-
#
|
|
83
|
+
# otherwise
|
|
96
84
|
#
|
|
97
85
|
# @api private
|
|
98
86
|
#
|
|
99
87
|
def receiver?(node)
|
|
100
|
-
receiver = node.
|
|
101
|
-
case receiver
|
|
102
|
-
when
|
|
88
|
+
receiver = node.children[RECEIVER_INDEX]
|
|
89
|
+
case receiver.type
|
|
90
|
+
when :self
|
|
103
91
|
true
|
|
104
|
-
when
|
|
92
|
+
when :const
|
|
105
93
|
receiver_name?(receiver)
|
|
106
94
|
else
|
|
107
|
-
$stderr.puts "
|
|
95
|
+
$stderr.puts "Can only match self or const, got #{receiver.type}, unable to match receiver of defs node"
|
|
108
96
|
false
|
|
109
97
|
end
|
|
110
98
|
end
|
|
111
99
|
|
|
112
100
|
# Test if reciver name matches context
|
|
113
101
|
#
|
|
114
|
-
# @param [
|
|
102
|
+
# @param [Parser::AST::Node] node
|
|
115
103
|
#
|
|
116
104
|
# @return [true]
|
|
117
|
-
#
|
|
105
|
+
# if node name matches unqualified scope name
|
|
118
106
|
#
|
|
119
107
|
# @return [false]
|
|
120
|
-
#
|
|
108
|
+
# otherwise
|
|
121
109
|
#
|
|
122
110
|
# @api private
|
|
123
111
|
#
|
|
124
112
|
def receiver_name?(node)
|
|
125
|
-
|
|
113
|
+
name = node.children[CONST_NAME_INDEX]
|
|
114
|
+
name.to_s == context.unqualified_name
|
|
126
115
|
end
|
|
127
116
|
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
117
|
+
end # Singleton
|
|
118
|
+
end # Method
|
|
119
|
+
end # Matcher
|
|
120
|
+
end # Mutant
|
|
@@ -2,21 +2,9 @@ module Mutant
|
|
|
2
2
|
class Matcher
|
|
3
3
|
# Matcher for subjects that are a specific method
|
|
4
4
|
class Method < self
|
|
5
|
-
include Adamantium::Flat,
|
|
5
|
+
include Adamantium::Flat, Concord::Public.new(:scope, :method)
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# @param [String] input
|
|
10
|
-
#
|
|
11
|
-
# @return [Matcher::Method]
|
|
12
|
-
#
|
|
13
|
-
# @api private
|
|
14
|
-
#
|
|
15
|
-
def self.parse(input)
|
|
16
|
-
Classifier.run(input)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Methods within rbx kernel directory are precompiled and their source
|
|
7
|
+
# Methods within rbx kernel directory are precompiled and their source
|
|
20
8
|
# cannot be accessed via reading source location
|
|
21
9
|
BLACKLIST = /\Akernel\//.freeze
|
|
22
10
|
|
|
@@ -29,74 +17,20 @@ module Mutant
|
|
|
29
17
|
# returns self when block given
|
|
30
18
|
#
|
|
31
19
|
# @api private
|
|
32
|
-
#
|
|
20
|
+
#
|
|
33
21
|
def each(&block)
|
|
34
22
|
return to_enum unless block_given?
|
|
35
23
|
|
|
36
24
|
return self if skip?
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
end
|
|
26
|
+
util = subject
|
|
27
|
+
yield util if util
|
|
41
28
|
|
|
42
29
|
self
|
|
43
30
|
end
|
|
44
31
|
|
|
45
|
-
# Return method
|
|
46
|
-
#
|
|
47
|
-
# @return [UnboundMethod, Method]
|
|
48
|
-
#
|
|
49
|
-
# @api private
|
|
50
|
-
#
|
|
51
|
-
attr_reader :method
|
|
52
|
-
|
|
53
|
-
# Return scope
|
|
54
|
-
#
|
|
55
|
-
# @return [Class|Module]
|
|
56
|
-
#
|
|
57
|
-
# @api private
|
|
58
|
-
#
|
|
59
|
-
attr_reader :scope
|
|
60
|
-
|
|
61
|
-
# Return method name
|
|
62
|
-
#
|
|
63
|
-
# @return [String]
|
|
64
|
-
#
|
|
65
|
-
# @api private
|
|
66
|
-
#
|
|
67
|
-
def method_name
|
|
68
|
-
method.name
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Test if method is public
|
|
72
|
-
#
|
|
73
|
-
# @return [true]
|
|
74
|
-
# if method is public
|
|
75
|
-
#
|
|
76
|
-
# @return [false]
|
|
77
|
-
# otherwise
|
|
78
|
-
#
|
|
79
|
-
# @api private
|
|
80
|
-
#
|
|
81
|
-
abstract_method :public?
|
|
82
|
-
|
|
83
32
|
private
|
|
84
33
|
|
|
85
|
-
# Initialize method filter
|
|
86
|
-
#
|
|
87
|
-
# @param [Class|Module] scope
|
|
88
|
-
# @param [Method, UnboundMethod] method
|
|
89
|
-
#
|
|
90
|
-
# @return [undefined]
|
|
91
|
-
#
|
|
92
|
-
# @api private
|
|
93
|
-
#
|
|
94
|
-
def initialize(scope, method)
|
|
95
|
-
@scope, @method = scope, method
|
|
96
|
-
# FIXME: cache public private should not be needed, loader should not override visibility! (But does currently) :(
|
|
97
|
-
public?
|
|
98
|
-
end
|
|
99
|
-
|
|
100
34
|
# Test if method is skipped
|
|
101
35
|
#
|
|
102
36
|
# @return [true]
|
|
@@ -117,6 +51,16 @@ module Mutant
|
|
|
117
51
|
false
|
|
118
52
|
end
|
|
119
53
|
|
|
54
|
+
# Return method name
|
|
55
|
+
#
|
|
56
|
+
# @return [String]
|
|
57
|
+
#
|
|
58
|
+
# @api private
|
|
59
|
+
#
|
|
60
|
+
def method_name
|
|
61
|
+
method.name
|
|
62
|
+
end
|
|
63
|
+
|
|
120
64
|
# Return context
|
|
121
65
|
#
|
|
122
66
|
# @return [Context::Scope]
|
|
@@ -129,12 +73,12 @@ module Mutant
|
|
|
129
73
|
|
|
130
74
|
# Return full ast
|
|
131
75
|
#
|
|
132
|
-
# @return [
|
|
76
|
+
# @return [Parser::AST::Node]
|
|
133
77
|
#
|
|
134
78
|
# @api private
|
|
135
79
|
#
|
|
136
80
|
def ast
|
|
137
|
-
File.read(source_path)
|
|
81
|
+
Parser::CurrentRuby.parse(File.read(source_path))
|
|
138
82
|
end
|
|
139
83
|
|
|
140
84
|
# Return path to source
|
|
@@ -180,24 +124,92 @@ module Mutant
|
|
|
180
124
|
def subject
|
|
181
125
|
node = matched_node
|
|
182
126
|
return unless node
|
|
183
|
-
|
|
127
|
+
self.class::SUBJECT_CLASS.new(context, node)
|
|
184
128
|
end
|
|
185
129
|
memoize :subject
|
|
186
130
|
|
|
131
|
+
# Visitor to find last match inside AST
|
|
132
|
+
class Finder
|
|
133
|
+
|
|
134
|
+
# Run finder
|
|
135
|
+
#
|
|
136
|
+
# @param [Parser::AST::Node]
|
|
137
|
+
#
|
|
138
|
+
# @return [Parser::AST::Node]
|
|
139
|
+
# if found
|
|
140
|
+
#
|
|
141
|
+
# @return [nil]
|
|
142
|
+
# otherwise
|
|
143
|
+
#
|
|
144
|
+
# @api private
|
|
145
|
+
#
|
|
146
|
+
#
|
|
147
|
+
def self.run(root, &predicate)
|
|
148
|
+
new(root, predicate).match
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private_class_method :new
|
|
152
|
+
|
|
153
|
+
# Return match
|
|
154
|
+
#
|
|
155
|
+
# @return [Parser::AST::Node]
|
|
156
|
+
#
|
|
157
|
+
# @api private
|
|
158
|
+
#
|
|
159
|
+
attr_reader :match
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
# Initialize object
|
|
164
|
+
#
|
|
165
|
+
# @param [Parer::AST::Node]
|
|
166
|
+
#
|
|
167
|
+
# @return [undefined]
|
|
168
|
+
#
|
|
169
|
+
# @api private
|
|
170
|
+
#
|
|
171
|
+
#
|
|
172
|
+
def initialize(root, predicate)
|
|
173
|
+
@root, @predicate = root, predicate
|
|
174
|
+
visit(root)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Visit node
|
|
178
|
+
#
|
|
179
|
+
# @param [Parser::AST::Node] node
|
|
180
|
+
#
|
|
181
|
+
# @return [undefined]
|
|
182
|
+
#
|
|
183
|
+
# @api private
|
|
184
|
+
#
|
|
185
|
+
def visit(node)
|
|
186
|
+
if @predicate.call(node)
|
|
187
|
+
@match = node
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
node.children.each do |child|
|
|
191
|
+
visit(child) if child.kind_of?(Parser::AST::Node)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
end # Finder
|
|
196
|
+
|
|
187
197
|
# Return matched node
|
|
188
198
|
#
|
|
189
|
-
# @return [
|
|
199
|
+
# @return [Parser::AST::Node]
|
|
200
|
+
# if node could be found
|
|
201
|
+
#
|
|
202
|
+
# @return [nil]
|
|
203
|
+
# otherwise
|
|
190
204
|
#
|
|
191
205
|
# @api private
|
|
192
206
|
#
|
|
193
207
|
def matched_node
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
last_match = node if match?(node)
|
|
197
|
-
predicate
|
|
208
|
+
Finder.run(ast) do |node|
|
|
209
|
+
match?(node)
|
|
198
210
|
end
|
|
199
|
-
last_match
|
|
200
211
|
end
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
end
|
|
212
|
+
|
|
213
|
+
end # Method
|
|
214
|
+
end # Matcher
|
|
215
|
+
end # Mutant
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
module Mutant
|
|
2
2
|
class Matcher
|
|
3
|
-
# Abstract base class for matcher that returns subjects extracted from scope
|
|
4
|
-
class
|
|
5
|
-
include AbstractType
|
|
6
|
-
|
|
7
|
-
# Return scope
|
|
8
|
-
#
|
|
9
|
-
# @return [Class,Model]
|
|
10
|
-
#
|
|
11
|
-
# @api private
|
|
12
|
-
#
|
|
13
|
-
attr_reader :scope
|
|
3
|
+
# Abstract base class for matcher that returns method subjects extracted from scope
|
|
4
|
+
class Methods < self
|
|
5
|
+
include AbstractType, Concord::Public.new(:scope)
|
|
14
6
|
|
|
15
7
|
# Enumerate subjects
|
|
16
8
|
#
|
|
17
9
|
# @return [self]
|
|
18
10
|
# if block given
|
|
19
11
|
#
|
|
20
|
-
# @return [Enumerator<Subject>]
|
|
12
|
+
# @return [Enumerator<Subject>]
|
|
21
13
|
# otherwise
|
|
22
14
|
#
|
|
23
15
|
# @api private
|
|
@@ -32,19 +24,6 @@ module Mutant
|
|
|
32
24
|
self
|
|
33
25
|
end
|
|
34
26
|
|
|
35
|
-
# Return methods
|
|
36
|
-
#
|
|
37
|
-
# @return [Enumerable<Method, UnboundMethod>]
|
|
38
|
-
#
|
|
39
|
-
# @api private
|
|
40
|
-
#
|
|
41
|
-
def methods
|
|
42
|
-
method_names.map do |name|
|
|
43
|
-
access(name)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
memoize :methods
|
|
47
|
-
|
|
48
27
|
# Return method matcher class
|
|
49
28
|
#
|
|
50
29
|
# @return [Class:Matcher::Method]
|
|
@@ -55,59 +34,66 @@ module Mutant
|
|
|
55
34
|
self.class::MATCHER
|
|
56
35
|
end
|
|
57
36
|
|
|
58
|
-
# Return
|
|
59
|
-
#
|
|
60
|
-
# @param [Object] object
|
|
37
|
+
# Return methods
|
|
61
38
|
#
|
|
62
|
-
# @return [Enumerable<
|
|
39
|
+
# @return [Enumerable<Method, UnboundMethod>]
|
|
63
40
|
#
|
|
64
41
|
# @api private
|
|
65
42
|
#
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
def methods
|
|
44
|
+
candidate_names.each_with_object([]) do |name, methods|
|
|
45
|
+
method = access(name)
|
|
46
|
+
methods << method if method.owner == candidate_scope
|
|
47
|
+
end
|
|
70
48
|
end
|
|
49
|
+
memoize :methods
|
|
71
50
|
|
|
72
51
|
private
|
|
73
52
|
|
|
74
|
-
#
|
|
53
|
+
# Emit matches for method
|
|
75
54
|
#
|
|
76
|
-
# @param [
|
|
55
|
+
# @param [UnboundMethod, Method] method
|
|
77
56
|
#
|
|
78
57
|
# @return [undefined]
|
|
79
58
|
#
|
|
80
59
|
# @api private
|
|
81
60
|
#
|
|
82
|
-
def
|
|
83
|
-
|
|
61
|
+
def emit_matches(method)
|
|
62
|
+
matcher.new(scope, method).each do |subject|
|
|
63
|
+
yield subject
|
|
64
|
+
end
|
|
84
65
|
end
|
|
85
66
|
|
|
86
|
-
#
|
|
67
|
+
# Return candidate names
|
|
87
68
|
#
|
|
88
|
-
# @param [
|
|
69
|
+
# @param [Object] object
|
|
89
70
|
#
|
|
90
|
-
# @return [
|
|
71
|
+
# @return [Enumerable<Symbol>]
|
|
91
72
|
#
|
|
92
73
|
# @api private
|
|
93
74
|
#
|
|
94
|
-
def
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
75
|
+
def candidate_names
|
|
76
|
+
names =
|
|
77
|
+
candidate_scope.public_instance_methods(false) +
|
|
78
|
+
candidate_scope.private_instance_methods(false) +
|
|
79
|
+
candidate_scope.protected_instance_methods(false)
|
|
80
|
+
names.sort
|
|
98
81
|
end
|
|
99
82
|
|
|
100
|
-
# Return
|
|
83
|
+
# Return candidate scope
|
|
101
84
|
#
|
|
102
|
-
# @return [
|
|
85
|
+
# @return [Class, Module]
|
|
103
86
|
#
|
|
104
87
|
# @api private
|
|
105
88
|
#
|
|
106
|
-
abstract_method :
|
|
89
|
+
abstract_method :candidate_scope
|
|
107
90
|
|
|
91
|
+
# Matcher for singleton methods
|
|
108
92
|
class Singleton < self
|
|
109
93
|
MATCHER = Matcher::Method::Singleton
|
|
110
94
|
|
|
95
|
+
private
|
|
96
|
+
|
|
111
97
|
# Return method for name
|
|
112
98
|
#
|
|
113
99
|
# @param [Symbol] method_name
|
|
@@ -120,29 +106,25 @@ module Mutant
|
|
|
120
106
|
scope.method(method_name)
|
|
121
107
|
end
|
|
122
108
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# Return singleton methods defined on scope
|
|
126
|
-
#
|
|
127
|
-
# @param [Class|Module] scope
|
|
109
|
+
# Return candidate scope
|
|
128
110
|
#
|
|
129
|
-
# @return [
|
|
111
|
+
# @return [Class]
|
|
130
112
|
#
|
|
131
113
|
# @api private
|
|
132
114
|
#
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
names = self.class.method_names(singleton_class)
|
|
136
|
-
|
|
137
|
-
names.sort.reject do |name|
|
|
138
|
-
name.to_sym == :__class_init__
|
|
139
|
-
end
|
|
115
|
+
def candidate_scope
|
|
116
|
+
scope.singleton_class
|
|
140
117
|
end
|
|
141
|
-
|
|
118
|
+
memoize :candidate_scope, :freezer => :noop
|
|
142
119
|
|
|
120
|
+
end # Singleton
|
|
121
|
+
|
|
122
|
+
# Matcher for instance methods
|
|
143
123
|
class Instance < self
|
|
144
124
|
MATCHER = Matcher::Method::Instance
|
|
145
125
|
|
|
126
|
+
private
|
|
127
|
+
|
|
146
128
|
# Return method for name
|
|
147
129
|
#
|
|
148
130
|
# @param [Symbol] method_name
|
|
@@ -155,23 +137,18 @@ module Mutant
|
|
|
155
137
|
scope.instance_method(method_name)
|
|
156
138
|
end
|
|
157
139
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
# Return instance methods names of scope
|
|
161
|
-
#
|
|
162
|
-
# @param [Class|Module] scope
|
|
140
|
+
# Return candidate scope
|
|
163
141
|
#
|
|
164
|
-
# @return [
|
|
142
|
+
# @return [Class, Module]
|
|
165
143
|
#
|
|
166
144
|
# @api private
|
|
167
145
|
#
|
|
168
|
-
def
|
|
169
|
-
scope
|
|
170
|
-
return [] unless scope.kind_of?(Module)
|
|
171
|
-
names = self.class.method_names(scope)
|
|
172
|
-
names.uniq.sort
|
|
146
|
+
def candidate_scope
|
|
147
|
+
scope
|
|
173
148
|
end
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
149
|
+
|
|
150
|
+
end # Instance
|
|
151
|
+
|
|
152
|
+
end # Methods
|
|
153
|
+
end # Matcher
|
|
154
|
+
end # Mutant
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Matcher
|
|
3
|
+
|
|
4
|
+
# Matcher for specific namespace
|
|
5
|
+
class Namespace < self
|
|
6
|
+
include Concord::Public.new(:namespace)
|
|
7
|
+
|
|
8
|
+
# Enumerate subjects
|
|
9
|
+
#
|
|
10
|
+
# @return [self]
|
|
11
|
+
# if block given
|
|
12
|
+
#
|
|
13
|
+
# @return [Enumerator<Subject>]
|
|
14
|
+
# otherwise
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
#
|
|
18
|
+
def each(&block)
|
|
19
|
+
return to_enum unless block_given?
|
|
20
|
+
|
|
21
|
+
scopes.each do |scope|
|
|
22
|
+
Scope.each(scope, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Return pattern
|
|
31
|
+
#
|
|
32
|
+
# @return [Regexp]
|
|
33
|
+
#
|
|
34
|
+
# @api private
|
|
35
|
+
#
|
|
36
|
+
def pattern
|
|
37
|
+
%r(\A#{Regexp.escape(namespace.name)}(?:::)?)
|
|
38
|
+
end
|
|
39
|
+
memoize :pattern
|
|
40
|
+
|
|
41
|
+
# Return scope enumerator
|
|
42
|
+
#
|
|
43
|
+
# @return [Enumerable<Object>]
|
|
44
|
+
#
|
|
45
|
+
# @api private
|
|
46
|
+
#
|
|
47
|
+
def scopes(&block)
|
|
48
|
+
return to_enum(__method__) unless block_given?
|
|
49
|
+
|
|
50
|
+
::ObjectSpace.each_object(Module).each do |scope|
|
|
51
|
+
emit_scope(scope, &block)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Yield scope if name matches pattern
|
|
56
|
+
#
|
|
57
|
+
# @param [Module,Class] scope
|
|
58
|
+
#
|
|
59
|
+
# @return [undefined]
|
|
60
|
+
#
|
|
61
|
+
# @api private
|
|
62
|
+
#
|
|
63
|
+
def emit_scope(scope)
|
|
64
|
+
if pattern =~ scope.name
|
|
65
|
+
yield scope
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end # Namespace
|
|
70
|
+
end # Matcher
|
|
71
|
+
end # Mutant
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Matcher
|
|
3
|
+
# Matcher for specific namespace
|
|
4
|
+
class Scope < self
|
|
5
|
+
include Concord::Public.new(:scope)
|
|
6
|
+
|
|
7
|
+
MATCHERS = [
|
|
8
|
+
Matcher::Methods::Singleton,
|
|
9
|
+
Matcher::Methods::Instance
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
# Enumerate subjects
|
|
13
|
+
#
|
|
14
|
+
# @return [self]
|
|
15
|
+
# if block given
|
|
16
|
+
#
|
|
17
|
+
# @return [Enumerator<Subject>]
|
|
18
|
+
# otherwise
|
|
19
|
+
#
|
|
20
|
+
# @api private
|
|
21
|
+
#
|
|
22
|
+
def each(&block)
|
|
23
|
+
return to_enum unless block_given?
|
|
24
|
+
|
|
25
|
+
MATCHERS.each do |matcher|
|
|
26
|
+
matcher.each(scope, &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end # Scope
|
|
33
|
+
end # Matcher
|
|
34
|
+
end # Mutant
|