mutant 0.2.20 → 0.3.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +10 -11
  3. data/Changelog.md +93 -38
  4. data/Gemfile +3 -1
  5. data/Gemfile.devtools +16 -20
  6. data/Guardfile +1 -1
  7. data/README.md +36 -16
  8. data/Rakefile +21 -2
  9. data/TODO +11 -7
  10. data/bin/mutant +4 -0
  11. data/bin/zombie +4 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +2 -2
  14. data/config/flog.yml +1 -1
  15. data/config/{site.reek → reek.yml} +94 -70
  16. data/lib/mutant/cli/classifier/method.rb +100 -0
  17. data/lib/mutant/cli/classifier/namespace.rb +47 -0
  18. data/lib/mutant/cli/classifier/scope.rb +35 -0
  19. data/lib/mutant/cli/classifier.rb +141 -0
  20. data/lib/mutant/cli.rb +115 -162
  21. data/lib/mutant/color.rb +2 -2
  22. data/lib/mutant/config.rb +27 -0
  23. data/lib/mutant/constants.rb +32 -17
  24. data/lib/mutant/context/scope.rb +33 -51
  25. data/lib/mutant/context.rb +8 -19
  26. data/lib/mutant/differ.rb +5 -5
  27. data/lib/mutant/helper.rb +2 -17
  28. data/lib/mutant/killer/forked.rb +44 -0
  29. data/lib/mutant/killer/forking.rb +3 -57
  30. data/lib/mutant/killer/rspec.rb +16 -20
  31. data/lib/mutant/killer/static.rb +6 -7
  32. data/lib/mutant/killer.rb +48 -74
  33. data/lib/mutant/loader.rb +6 -6
  34. data/lib/mutant/matcher/chain.rb +4 -25
  35. data/lib/mutant/matcher/method/instance.rb +14 -24
  36. data/lib/mutant/matcher/method/singleton.rb +35 -46
  37. data/lib/mutant/matcher/method.rb +95 -83
  38. data/lib/mutant/matcher/{scope_methods.rb → methods.rb} +53 -76
  39. data/lib/mutant/matcher/namespace.rb +71 -0
  40. data/lib/mutant/matcher/scope.rb +34 -0
  41. data/lib/mutant/matcher.rb +24 -34
  42. data/lib/mutant/mutation/evil.rb +35 -0
  43. data/lib/mutant/mutation/filter/code.rb +7 -28
  44. data/lib/mutant/mutation/filter/regexp.rb +6 -18
  45. data/lib/mutant/mutation/filter/whitelist.rb +5 -4
  46. data/lib/mutant/mutation/filter.rb +10 -9
  47. data/lib/mutant/mutation/neutral.rb +35 -0
  48. data/lib/mutant/mutation.rb +21 -61
  49. data/lib/mutant/mutator/node/argument.rb +88 -0
  50. data/lib/mutant/mutator/node/arguments.rb +52 -0
  51. data/lib/mutant/mutator/node/assignment.rb +34 -38
  52. data/lib/mutant/mutator/node/begin.rb +33 -0
  53. data/lib/mutant/mutator/node/block.rb +14 -14
  54. data/lib/mutant/mutator/node/case.rb +59 -0
  55. data/lib/mutant/mutator/node/define.rb +26 -22
  56. data/lib/mutant/mutator/node/if.rb +31 -71
  57. data/lib/mutant/mutator/node/literal/array.rb +25 -9
  58. data/lib/mutant/mutator/node/literal/boolean.rb +13 -30
  59. data/lib/mutant/mutator/node/literal/dynamic.rb +6 -5
  60. data/lib/mutant/mutator/node/literal/fixnum.rb +18 -7
  61. data/lib/mutant/mutator/node/literal/float.rb +15 -8
  62. data/lib/mutant/mutator/node/literal/hash.rb +33 -52
  63. data/lib/mutant/mutator/node/literal/nil.rb +8 -7
  64. data/lib/mutant/mutator/node/literal/range.rb +25 -50
  65. data/lib/mutant/mutator/node/literal/regex.rb +15 -23
  66. data/lib/mutant/mutator/node/literal/string.rb +7 -6
  67. data/lib/mutant/mutator/node/literal/symbol.rb +7 -6
  68. data/lib/mutant/mutator/node/literal.rb +4 -46
  69. data/lib/mutant/mutator/node/mlhs.rb +27 -0
  70. data/lib/mutant/mutator/node/noop.rb +18 -43
  71. data/lib/mutant/mutator/node/return.rb +8 -8
  72. data/lib/mutant/mutator/node/send/binary.rb +31 -0
  73. data/lib/mutant/mutator/node/send.rb +106 -72
  74. data/lib/mutant/mutator/node/super.rb +15 -20
  75. data/lib/mutant/mutator/node/when.rb +32 -7
  76. data/lib/mutant/mutator/node/while.rb +9 -7
  77. data/lib/mutant/mutator/node.rb +116 -66
  78. data/lib/mutant/mutator/registry.rb +14 -11
  79. data/lib/mutant/mutator/util/array.rb +9 -9
  80. data/lib/mutant/mutator/util/symbol.rb +6 -20
  81. data/lib/mutant/mutator/util.rb +6 -3
  82. data/lib/mutant/mutator.rb +12 -28
  83. data/lib/mutant/node_helpers.rb +28 -0
  84. data/lib/mutant/random.rb +3 -2
  85. data/lib/mutant/reporter/cli/printer/config.rb +174 -0
  86. data/lib/mutant/reporter/cli/printer/killer.rb +42 -0
  87. data/lib/mutant/reporter/cli/printer/mutation.rb +55 -0
  88. data/lib/mutant/reporter/cli/printer/subject.rb +147 -0
  89. data/lib/mutant/reporter/cli/printer.rb +165 -0
  90. data/lib/mutant/reporter/cli.rb +9 -277
  91. data/lib/mutant/reporter/null.rb +6 -30
  92. data/lib/mutant/reporter.rb +6 -73
  93. data/lib/mutant/runner/config.rb +82 -0
  94. data/lib/mutant/runner/mutation.rb +58 -0
  95. data/lib/mutant/runner/subject.rb +81 -0
  96. data/lib/mutant/runner.rb +42 -92
  97. data/lib/mutant/singleton_methods.rb +2 -2
  98. data/lib/mutant/strategy/method_expansion.rb +51 -0
  99. data/lib/mutant/strategy/rspec/dm2/lookup/method.rb +142 -0
  100. data/lib/mutant/strategy/rspec/dm2/lookup.rb +61 -0
  101. data/lib/mutant/strategy/rspec/dm2.rb +22 -0
  102. data/lib/mutant/strategy/rspec.rb +20 -22
  103. data/lib/mutant/strategy/static.rb +18 -0
  104. data/lib/mutant/strategy.rb +15 -50
  105. data/lib/mutant/subject/method.rb +100 -0
  106. data/lib/mutant/subject.rb +18 -49
  107. data/lib/mutant/support/method_object.rb +4 -2
  108. data/lib/mutant.rb +40 -35
  109. data/mutant.gemspec +9 -8
  110. data/spec/integration/mutant/rspec_killer_spec.rb +3 -3
  111. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +9 -0
  112. data/spec/integration/mutant/zombie_spec.rb +1 -1
  113. data/spec/shared/method_matcher_behavior.rb +35 -0
  114. data/spec/shared/mutator_behavior.rb +63 -32
  115. data/spec/spec_helper.rb +13 -3
  116. data/spec/support/ice_nine_config.rb +8 -0
  117. data/spec/support/rspec.rb +1 -1
  118. data/spec/support/zombie.rb +1 -1
  119. data/spec/unit/mutant/cli/class_methods/new_spec.rb +42 -28
  120. data/spec/unit/mutant/cli/class_methods/run_spec.rb +15 -13
  121. data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +44 -0
  122. data/spec/unit/mutant/context/scope/root_spec.rb +4 -4
  123. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +6 -5
  124. data/spec/unit/mutant/killer/success_predicate_spec.rb +28 -0
  125. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +1 -1
  126. data/spec/unit/mutant/matcher/chain/each_spec.rb +1 -1
  127. data/spec/unit/mutant/matcher/chain/matchers_spec.rb +1 -1
  128. data/spec/unit/mutant/matcher/method/instance/each_spec.rb +112 -0
  129. data/spec/unit/mutant/matcher/method/singleton/each_spec.rb +93 -0
  130. data/spec/unit/mutant/matcher/methods/instance/each_spec.rb +59 -0
  131. data/spec/unit/mutant/matcher/methods/singleton/each_spec.rb +53 -0
  132. data/spec/unit/mutant/matcher/namespace/each_spec.rb +37 -0
  133. data/spec/unit/mutant/mutator/node/begin/mutation_spec.rb +33 -0
  134. data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +42 -14
  135. data/spec/unit/mutant/mutator/node/case/mutation_spec.rb +319 -0
  136. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +31 -27
  137. data/spec/unit/mutant/mutator/node/if/mutation_spec.rb +75 -0
  138. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +1 -1
  139. data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +2 -2
  140. data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +1 -3
  141. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +1 -9
  142. data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +6 -2
  143. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +111 -108
  144. data/spec/unit/mutant/mutator/node/super/mutation_spec.rb +0 -33
  145. data/spec/unit/mutant/mutator/node/while/mutation_spec.rb +2 -2
  146. data/spec/unit/mutant/runner/config/subjects_spec.rb +38 -0
  147. data/spec/unit/mutant/runner/config/success_predicate_spec.rb +53 -0
  148. data/spec/unit/mutant/runner/failed_predicte_spec.rb +33 -0
  149. data/spec/unit/mutant/runner/mutation/killer_spec.rb +39 -0
  150. data/spec/unit/mutant/runner/subject/success_predicate_spec.rb +49 -0
  151. data/spec/unit/mutant/strategy/method_expansion/class_methods/run_spec.rb +49 -0
  152. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +52 -0
  153. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +42 -0
  154. data/spec/unit/mutant/subject/context_spec.rb +6 -3
  155. data/spec/unit/mutant/subject/each_spec.rb +11 -8
  156. data/spec/unit/mutant/subject/node_spec.rb +6 -2
  157. data/test_app/spec/shared/method_filter_parse_behavior.rb +0 -2
  158. data/test_app/spec/shared/method_match_behavior.rb +1 -1
  159. data/test_app/spec/spec_helper.rb +4 -2
  160. metadata +101 -109
  161. data/config/roodi.yml +0 -26
  162. data/lib/mutant/matcher/method/classifier.rb +0 -141
  163. data/lib/mutant/matcher/object_space.rb +0 -114
  164. data/lib/mutant/mutator/node/actual_arguments.rb +0 -25
  165. data/lib/mutant/mutator/node/default_arguments.rb +0 -25
  166. data/lib/mutant/mutator/node/formal_arguments_19/default_mutations.rb +0 -33
  167. data/lib/mutant/mutator/node/formal_arguments_19/pattern_argument_expansion.rb +0 -35
  168. data/lib/mutant/mutator/node/formal_arguments_19/require_defaults.rb +0 -37
  169. data/lib/mutant/mutator/node/formal_arguments_19.rb +0 -41
  170. data/lib/mutant/mutator/node/iter_19.rb +0 -27
  171. data/lib/mutant/mutator/node/literal/empty_array.rb +0 -26
  172. data/lib/mutant/mutator/node/pattern_arguments.rb +0 -41
  173. data/lib/mutant/mutator/node/pattern_variable.rb +0 -23
  174. data/lib/mutant/mutator/node/receiver_case.rb +0 -122
  175. data/lib/mutant/mutator/node/send/binary_operator_method.rb +0 -61
  176. data/lib/mutant/mutator/node/send/with_arguments.rb +0 -81
  177. data/lib/mutant/reporter/stats.rb +0 -120
  178. data/lib/mutant/strategy/rspec/example_lookup.rb +0 -163
  179. data/spec/integration/mutant/method_matching_spec.rb +0 -269
  180. data/spec/shared/method_match_behavior.rb +0 -39
  181. data/spec/unit/mutant/killer/fail_ques_spec.rb +0 -39
  182. data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +0 -49
  183. data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +0 -12
  184. data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +0 -21
  185. data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +0 -52
  186. data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +0 -24
  187. data/spec/unit/mutant/matcher/object_space/each_spec.rb +0 -31
  188. data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +0 -60
  189. data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +0 -27
  190. data/spec/unit/mutant/strategy/rspec/example_lookup/spec_file_spec.rb +0 -236
  191. data/spec/unit/mutant/subject/class_methods/new_spec.rb +0 -13
  192. data/tasks/metrics/ci.rake +0 -7
  193. data/tasks/metrics/flay.rake +0 -41
  194. data/tasks/metrics/flog.rake +0 -43
  195. data/tasks/metrics/heckle.rake +0 -216
  196. data/tasks/metrics/metric_fu.rake +0 -31
  197. data/tasks/metrics/reek.rake +0 -15
  198. data/tasks/metrics/roodi.rake +0 -15
  199. data/tasks/metrics/yardstick.rake +0 -23
  200. data/tasks/spec.rake +0 -45
  201. 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
- # Test if method is public
19
- #
20
- # @return [true]
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 [Rubinius::AST::Node] node
27
+ # @param [Parser::AST::Node] node
39
28
  #
40
29
  # @return [true]
41
- # returns true if node matches method
30
+ # if node matches method
42
31
  #
43
32
  # @return [false]
44
- # returns false if node NOT matches method
33
+ # otherwise
45
34
  #
46
35
  # @api private
47
36
  #
48
37
  def match?(node)
49
- node.class == Rubinius::AST::DefineSingleton &&
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 [Rubinius::AST::Node] node
43
+ # @param [Parser::AST::Node] node
58
44
  #
59
45
  # @return [true]
60
- # returns true if node matches source line
46
+ # if node matches source line
61
47
  #
62
48
  # @return [false]
63
- # returns false otherwise
49
+ # otherwise
64
50
  #
65
51
  # @api private
66
52
  #
67
53
  def line?(node)
68
- node.line == source_line
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 [Rubinius::AST::DefineSingleton] node
61
+ # @param [Parser::AST::Node] node
74
62
  #
75
63
  # @return [true]
76
- # returns true if node name matches
64
+ # if node name matches
77
65
  #
78
66
  # @return [false]
79
- # returns false otherwise
67
+ # otherwise
80
68
  #
81
69
  # @api private
82
70
  #
83
71
  def name?(node)
84
- node.body.name == method_name
72
+ node.children[NAME_INDEX] == method_name
85
73
  end
86
74
 
87
75
  # Test for receiver match
88
76
  #
89
- # @param [Rubinius::AST::DefineSingleton] node
77
+ # @param [Parser::AST::Node] node
90
78
  #
91
79
  # @return [true]
92
- # returns true when receiver is self or scope from pattern
80
+ # when receiver matches
93
81
  #
94
82
  # @return [false]
95
- # returns false otherwise
83
+ # otherwise
96
84
  #
97
85
  # @api private
98
86
  #
99
87
  def receiver?(node)
100
- receiver = node.receiver
101
- case receiver
102
- when Rubinius::AST::Self
88
+ receiver = node.children[RECEIVER_INDEX]
89
+ case receiver.type
90
+ when :self
103
91
  true
104
- when Rubinius::AST::ConstantAccess
92
+ when :const
105
93
  receiver_name?(receiver)
106
94
  else
107
- $stderr.puts "Unable to find singleton method definition only match receiver on Rubinius::AST::Self or Rubinius::AST::ConstantAccess, got #{receiver.class}"
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 [Rubinius::AST::Node] node
102
+ # @param [Parser::AST::Node] node
115
103
  #
116
104
  # @return [true]
117
- # returns true when node name matches unqualified scope name
105
+ # if node name matches unqualified scope name
118
106
  #
119
107
  # @return [false]
120
- # returns false otherwise
108
+ # otherwise
121
109
  #
122
110
  # @api private
123
111
  #
124
112
  def receiver_name?(node)
125
- node.name.to_s == context.unqualified_name
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, Equalizer.new(:identification)
5
+ include Adamantium::Flat, Concord::Public.new(:scope, :method)
6
6
 
7
- # Parse a method string into filter
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
- subject.tap do |subject|
39
- yield subject if subject
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 [Rubinius::AST::Node]
76
+ # @return [Parser::AST::Node]
133
77
  #
134
78
  # @api private
135
79
  #
136
80
  def ast
137
- File.read(source_path).to_ast
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
- Subject.new(self, context, node)
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 [Rubinus::AST::Node]
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
- last_match = nil
195
- ast.walk do |predicate, node|
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
- end
202
- end
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 methods
4
- class ScopeMethods < self
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 method names
59
- #
60
- # @param [Object] object
37
+ # Return methods
61
38
  #
62
- # @return [Enumerable<Symbol>]
39
+ # @return [Enumerable<Method, UnboundMethod>]
63
40
  #
64
41
  # @api private
65
42
  #
66
- def self.method_names(object)
67
- object.public_instance_methods(false) +
68
- object.private_instance_methods(false) +
69
- object.protected_instance_methods(false)
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
- # Initialize object
53
+ # Emit matches for method
75
54
  #
76
- # @param [Class,Module] scope
55
+ # @param [UnboundMethod, Method] method
77
56
  #
78
57
  # @return [undefined]
79
58
  #
80
59
  # @api private
81
60
  #
82
- def initialize(scope)
83
- @scope = scope
61
+ def emit_matches(method)
62
+ matcher.new(scope, method).each do |subject|
63
+ yield subject
64
+ end
84
65
  end
85
66
 
86
- # Emit matches for method
67
+ # Return candidate names
87
68
  #
88
- # @param [UnboundMethod, Method] method
69
+ # @param [Object] object
89
70
  #
90
- # @return [undefined]
71
+ # @return [Enumerable<Symbol>]
91
72
  #
92
73
  # @api private
93
74
  #
94
- def emit_matches(method)
95
- matcher.new(scope, method).each do |subject|
96
- yield subject
97
- end
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 method names
83
+ # Return candidate scope
101
84
  #
102
- # @return [Enumerable<Symbol>]
85
+ # @return [Class, Module]
103
86
  #
104
87
  # @api private
105
88
  #
106
- abstract_method :method_names
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
- private
124
-
125
- # Return singleton methods defined on scope
126
- #
127
- # @param [Class|Module] scope
109
+ # Return candidate scope
128
110
  #
129
- # @return [Enumerable<Symbol>]
111
+ # @return [Class]
130
112
  #
131
113
  # @api private
132
114
  #
133
- def method_names
134
- singleton_class = scope.singleton_class
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
- end
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
- private
159
-
160
- # Return instance methods names of scope
161
- #
162
- # @param [Class|Module] scope
140
+ # Return candidate scope
163
141
  #
164
- # @return [Enumerable<Symbol>]
142
+ # @return [Class, Module]
165
143
  #
166
144
  # @api private
167
145
  #
168
- def method_names
169
- scope = self.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
- end
175
- end
176
- end
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