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.
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