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.
Files changed (225) hide show
  1. data/.gitignore +5 -11
  2. data/.rspec +0 -1
  3. data/.travis.yml +14 -3
  4. data/Changelog.md +3 -0
  5. data/Gemfile +5 -1
  6. data/Gemfile.devtools +49 -0
  7. data/Guardfile +18 -0
  8. data/README.md +67 -0
  9. data/Rakefile +4 -1
  10. data/TODO +13 -0
  11. data/bin/mutant +14 -0
  12. data/bin/zombie +14 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/roodi.yml +26 -0
  16. data/config/site.reek +93 -0
  17. data/config/yardstick.yml +2 -0
  18. data/lib/inflector.rb +7 -0
  19. data/lib/inflector/defaults.rb +62 -0
  20. data/lib/inflector/inflections.rb +209 -0
  21. data/lib/inflector/methods.rb +149 -0
  22. data/lib/inflector/version.rb +3 -0
  23. data/lib/mutant.rb +96 -21
  24. data/lib/mutant/cli.rb +309 -0
  25. data/lib/mutant/color.rb +61 -0
  26. data/lib/mutant/context.rb +36 -0
  27. data/lib/mutant/context/scope.rb +138 -0
  28. data/lib/mutant/differ.rb +100 -0
  29. data/lib/mutant/helper.rb +38 -0
  30. data/lib/mutant/killer.rb +136 -0
  31. data/lib/mutant/killer/forking.rb +41 -0
  32. data/lib/mutant/killer/rspec.rb +49 -0
  33. data/lib/mutant/killer/static.rb +19 -0
  34. data/lib/mutant/loader.rb +129 -0
  35. data/lib/mutant/matcher.rb +55 -0
  36. data/lib/mutant/matcher/chain.rb +66 -0
  37. data/lib/mutant/matcher/method.rb +173 -0
  38. data/lib/mutant/matcher/method/classifier.rb +126 -0
  39. data/lib/mutant/matcher/method/instance.rb +67 -0
  40. data/lib/mutant/matcher/method/singleton.rb +141 -0
  41. data/lib/mutant/matcher/object_space.rb +114 -0
  42. data/lib/mutant/matcher/scope_methods.rb +127 -0
  43. data/lib/mutant/mutation.rb +101 -12
  44. data/lib/mutant/mutation/filter.rb +75 -0
  45. data/lib/mutant/mutation/filter/code.rb +68 -0
  46. data/lib/mutant/mutation/filter/regexp.rb +39 -0
  47. data/lib/mutant/mutation/filter/whitelist.rb +47 -0
  48. data/lib/mutant/mutator.rb +134 -30
  49. data/lib/mutant/mutator/node.rb +163 -0
  50. data/lib/mutant/mutator/node/arguments.rb +24 -0
  51. data/lib/mutant/mutator/node/block.rb +24 -0
  52. data/lib/mutant/mutator/node/define.rb +24 -0
  53. data/lib/mutant/mutator/node/if_statement.rb +93 -0
  54. data/lib/mutant/mutator/node/literal.rb +54 -0
  55. data/lib/mutant/mutator/node/literal/array.rb +28 -0
  56. data/lib/mutant/mutator/node/literal/boolean.rb +49 -0
  57. data/lib/mutant/mutator/node/literal/dynamic.rb +24 -0
  58. data/lib/mutant/mutator/node/literal/empty_array.rb +26 -0
  59. data/lib/mutant/mutator/node/literal/fixnum.rb +37 -0
  60. data/lib/mutant/mutator/node/literal/float.rb +48 -0
  61. data/lib/mutant/mutator/node/literal/hash.rb +89 -0
  62. data/lib/mutant/mutator/node/literal/nil.rb +25 -0
  63. data/lib/mutant/mutator/node/literal/range.rb +94 -0
  64. data/lib/mutant/mutator/node/literal/regex.rb +43 -0
  65. data/lib/mutant/mutator/node/literal/string.rb +26 -0
  66. data/lib/mutant/mutator/node/literal/symbol.rb +26 -0
  67. data/lib/mutant/mutator/node/noop.rb +55 -0
  68. data/lib/mutant/mutator/node/receiver_case.rb +140 -0
  69. data/lib/mutant/mutator/node/return.rb +31 -0
  70. data/lib/mutant/mutator/node/send.rb +112 -0
  71. data/lib/mutant/mutator/registry.rb +48 -0
  72. data/lib/mutant/mutator/util.rb +87 -0
  73. data/lib/mutant/random.rb +24 -27
  74. data/lib/mutant/reporter.rb +48 -30
  75. data/lib/mutant/reporter/cli.rb +221 -0
  76. data/lib/mutant/reporter/null.rb +42 -0
  77. data/lib/mutant/reporter/stats.rb +64 -0
  78. data/lib/mutant/runner.rb +112 -0
  79. data/lib/mutant/strategy.rb +42 -0
  80. data/lib/mutant/strategy/rspec.rb +59 -0
  81. data/lib/mutant/strategy/rspec/example_lookup.rb +122 -0
  82. data/lib/mutant/subject.rb +115 -0
  83. data/lib/mutant/support/method_object.rb +31 -0
  84. data/locator.rb +87 -0
  85. data/mutant.gemspec +21 -21
  86. data/spec/integration/mutant/differ_spec.rb +15 -0
  87. data/spec/integration/mutant/loader_spec.rb +21 -0
  88. data/spec/integration/mutant/method_matching_spec.rb +269 -0
  89. data/spec/integration/mutant/rspec_killer_spec.rb +24 -0
  90. data/spec/integration/mutant/runner_spec.rb +26 -0
  91. data/spec/integration/mutant/zombie_spec.rb +8 -0
  92. data/spec/rcov.opts +7 -0
  93. data/spec/shared/command_method_behavior.rb +7 -0
  94. data/spec/shared/each_method_behaviour.rb +15 -0
  95. data/spec/shared/hash_method_behavior.rb +17 -0
  96. data/spec/shared/idempotent_method_behavior.rb +7 -0
  97. data/spec/shared/invertible_method_behaviour.rb +9 -0
  98. data/spec/shared/method_filter_parse_behavior.rb +16 -0
  99. data/spec/shared/method_match_behavior.rb +39 -0
  100. data/spec/shared/mutator_behavior.rb +46 -0
  101. data/spec/spec_helper.rb +11 -14
  102. data/spec/support/compress_helper.rb +10 -0
  103. data/spec/support/rspec.rb +22 -0
  104. data/spec/support/test_app.rb +5 -0
  105. data/spec/support/zombie.rb +141 -0
  106. data/spec/unit/mutant/cli/class_methods/new_spec.rb +87 -0
  107. data/spec/unit/mutant/cli/class_methods/run_spec.rb +38 -0
  108. data/spec/unit/mutant/context/root_spec.rb +11 -0
  109. data/spec/unit/mutant/context/scope/class_methods/build_spec.rb +29 -0
  110. data/spec/unit/mutant/context/scope/root_spec.rb +22 -0
  111. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +27 -0
  112. data/spec/unit/mutant/killer/fail_ques_spec.rb +39 -0
  113. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +32 -0
  114. data/spec/unit/mutant/loader/eval/class_methods/run_spec.rb +33 -0
  115. data/spec/unit/mutant/loader/rubinius/class_methods/run_spec.rb +42 -0
  116. data/spec/unit/mutant/matcher/chain/each_spec.rb +37 -0
  117. data/spec/unit/mutant/matcher/chain/matchers_spec.rb +12 -0
  118. data/spec/unit/mutant/matcher/class_methods/from_string_spec.rb +49 -0
  119. data/spec/unit/mutant/matcher/class_methods/parse_spec.rb +12 -0
  120. data/spec/unit/mutant/matcher/each_spec.rb +14 -0
  121. data/spec/unit/mutant/matcher/method/class_methods/parse_spec.rb +21 -0
  122. data/spec/unit/mutant/matcher/method/classifier/class_methods/run_spec.rb +34 -0
  123. data/spec/unit/mutant/matcher/method/method_spec.rb +11 -0
  124. data/spec/unit/mutant/matcher/object_space/class_methods/parse_spec.rb +24 -0
  125. data/spec/unit/mutant/matcher/object_space/each_spec.rb +31 -0
  126. data/spec/unit/mutant/mutator/each_spec.rb +25 -0
  127. data/spec/unit/mutant/mutator/emit_new_spec.rb +51 -0
  128. data/spec/unit/mutant/mutator/emit_spec.rb +52 -0
  129. data/spec/unit/mutant/mutator/node/block/mutation_spec.rb +36 -0
  130. data/spec/unit/mutant/mutator/node/define/mutation_spec.rb +47 -0
  131. data/spec/unit/mutant/mutator/node/if_statement/mutation_spec.rb +30 -0
  132. data/spec/unit/mutant/mutator/node/literal/array_spec.rb +30 -0
  133. data/spec/unit/mutant/mutator/node/literal/boolean/mutation_spec.rb +23 -0
  134. data/spec/unit/mutant/mutator/node/literal/empty_array_spec.rb +17 -0
  135. data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +17 -0
  136. data/spec/unit/mutant/mutator/node/literal/float_spec.rb +25 -0
  137. data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +34 -0
  138. data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +13 -0
  139. data/spec/unit/mutant/mutator/node/literal/range_spec.rb +35 -0
  140. data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +23 -0
  141. data/spec/unit/mutant/mutator/node/literal/string_spec.rb +17 -0
  142. data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +17 -0
  143. data/spec/unit/mutant/mutator/node/receiver_case/mutation_spec.rb +27 -0
  144. data/spec/unit/mutant/mutator/node/return/mutation_spec.rb +21 -0
  145. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +78 -0
  146. data/spec/unit/mutant/mutator/self_spec.rb +7 -0
  147. data/spec/unit/mutant/subject/class_methods/new_spec.rb +13 -0
  148. data/spec/unit/mutant/subject/context_spec.rb +14 -0
  149. data/spec/unit/mutant/subject/each_spec.rb +35 -0
  150. data/spec/unit/mutant/subject/node_spec.rb +13 -0
  151. data/tasks/metrics/ci.rake +7 -0
  152. data/tasks/metrics/flay.rake +41 -0
  153. data/tasks/metrics/flog.rake +43 -0
  154. data/tasks/metrics/heckle.rake +216 -0
  155. data/tasks/metrics/metric_fu.rake +31 -0
  156. data/tasks/metrics/reek.rake +15 -0
  157. data/tasks/metrics/roodi.rake +15 -0
  158. data/tasks/metrics/yardstick.rake +23 -0
  159. data/tasks/spec.rake +45 -0
  160. data/tasks/yard.rake +9 -0
  161. data/test_app/.rspec +1 -0
  162. data/test_app/lib/test_app.rb +5 -0
  163. data/test_app/lib/test_app/literal.rb +32 -0
  164. data/test_app/spec/shared/command_method_behavior.rb +7 -0
  165. data/test_app/spec/shared/each_method_behaviour.rb +15 -0
  166. data/test_app/spec/shared/hash_method_behavior.rb +17 -0
  167. data/test_app/spec/shared/idempotent_method_behavior.rb +7 -0
  168. data/test_app/spec/shared/invertible_method_behaviour.rb +9 -0
  169. data/test_app/spec/shared/method_filter_parse_behavior.rb +16 -0
  170. data/test_app/spec/shared/method_match_behavior.rb +39 -0
  171. data/test_app/spec/shared/mutator_behavior.rb +44 -0
  172. data/test_app/spec/spec_helper.rb +7 -0
  173. data/test_app/spec/unit/test_app/literal/command_spec.rb +9 -0
  174. data/test_app/spec/unit/test_app/literal/string_spec.rb +9 -0
  175. metadata +346 -124
  176. data/.rvmrc +0 -1
  177. data/Readme.md +0 -13
  178. data/exe/mutate +0 -6
  179. data/lib/mutant/extensions.rb +0 -8
  180. data/lib/mutant/formatter.rb +0 -19
  181. data/lib/mutant/implementation.rb +0 -70
  182. data/lib/mutant/literal.rb +0 -147
  183. data/lib/mutant/method.rb +0 -31
  184. data/lib/mutant/mutatee.rb +0 -61
  185. data/lib/mutant/node.rb +0 -26
  186. data/lib/mutant/runners/rspec.rb +0 -34
  187. data/lib/mutant/version.rb +0 -3
  188. data/spec/functional/class_spec.rb +0 -46
  189. data/spec/functional/instance_method/array_spec.rb +0 -53
  190. data/spec/functional/instance_method/boolean_spec.rb +0 -101
  191. data/spec/functional/instance_method/call_spec.rb +0 -161
  192. data/spec/functional/instance_method/fixnum_spec.rb +0 -53
  193. data/spec/functional/instance_method/float_spec.rb +0 -53
  194. data/spec/functional/instance_method/hash_spec.rb +0 -53
  195. data/spec/functional/instance_method/if_spec.rb +0 -57
  196. data/spec/functional/instance_method/ivar_assign_spec.rb +0 -62
  197. data/spec/functional/instance_method/range_spec.rb +0 -53
  198. data/spec/functional/instance_method/regex_spec.rb +0 -55
  199. data/spec/functional/instance_method/string_spec.rb +0 -53
  200. data/spec/functional/instance_method/symbol_spec.rb +0 -53
  201. data/spec/functional/reporter/method_loaded_spec.rb +0 -62
  202. data/spec/functional/reporter/running_mutations_spec.rb +0 -60
  203. data/spec/functional/runners/rspec_spec.rb +0 -26
  204. data/spec/functional/singleton_method/array_spec.rb +0 -53
  205. data/spec/functional/singleton_method/boolean_spec.rb +0 -101
  206. data/spec/functional/singleton_method/call_spec.rb +0 -161
  207. data/spec/functional/singleton_method/fixnum_spec.rb +0 -53
  208. data/spec/functional/singleton_method/float_spec.rb +0 -53
  209. data/spec/functional/singleton_method/hash_spec.rb +0 -53
  210. data/spec/functional/singleton_method/if_spec.rb +0 -57
  211. data/spec/functional/singleton_method/ivar_assign_spec.rb +0 -60
  212. data/spec/functional/singleton_method/range_spec.rb +0 -53
  213. data/spec/functional/singleton_method/regex_spec.rb +0 -55
  214. data/spec/functional/singleton_method/string_spec.rb +0 -53
  215. data/spec/functional/singleton_method/symbol_spec.rb +0 -53
  216. data/spec/mutant/extensions_spec.rb +0 -13
  217. data/spec/mutant/implementation_spec.rb +0 -223
  218. data/spec/mutant/literal_spec.rb +0 -129
  219. data/spec/mutant/mutatee_spec.rb +0 -28
  220. data/spec/mutant/node_spec.rb +0 -41
  221. data/spec/mutant/random_spec.rb +0 -33
  222. data/spec/mutant/reporter_spec.rb +0 -17
  223. data/spec/mutant_spec.rb +0 -28
  224. data/spec/support/example_group_helpers.rb +0 -11
  225. data/spec/support/example_helpers.rb +0 -5
@@ -0,0 +1,309 @@
1
+ module Mutant
2
+ # Comandline parser
3
+ class CLI
4
+ include Adamantium::Flat, Equalizer.new(:matcher, :filter, :killer)
5
+
6
+ # Error raised when CLI argv is inalid
7
+ Error = Class.new(RuntimeError)
8
+
9
+ EXIT_FAILURE = 1
10
+ EXIT_SUCCESS = 0
11
+
12
+ # Run cli with arguments
13
+ #
14
+ # @param [Array<String>] arguments
15
+ #
16
+ # @return [Fixnum]
17
+ # returns exit status
18
+ #
19
+ # @api private
20
+ #
21
+ def self.run(*arguments)
22
+ error = Runner.run(new(*arguments)).fail?
23
+ error ? EXIT_FAILURE : EXIT_SUCCESS
24
+ rescue Error => exception
25
+ $stderr.puts(exception.message)
26
+ EXIT_FAILURE
27
+ end
28
+
29
+ # Return matcher
30
+ #
31
+ # @return [Mutant::Matcher]
32
+ #
33
+ # @raise [CLI::Error]
34
+ # raises error when matcher is not given
35
+ #
36
+ # @api private
37
+ #
38
+ def matcher
39
+ if @matchers.empty?
40
+ raise Error, 'No matchers given'
41
+ end
42
+
43
+ Mutant::Matcher::Chain.build(@matchers)
44
+ end
45
+ memoize :matcher
46
+
47
+ # Return mutation filter
48
+ #
49
+ # @return [Mutant::Matcher]
50
+ #
51
+ # @api private
52
+ #
53
+ def filter
54
+ if @filters.empty?
55
+ Mutation::Filter::ALL
56
+ else
57
+ Mutation::Filter::Whitelist.new(@filters)
58
+ end
59
+ end
60
+ memoize :filter
61
+
62
+ # Return stratety
63
+ #
64
+ # @return [Strategy]
65
+ #
66
+ # @api private
67
+ #
68
+ def strategy
69
+ @strategy || raise(Error, 'no strategy was set!')
70
+ end
71
+
72
+ # Return reporter
73
+ #
74
+ # @return [Mutant::Reporter::CLI]
75
+ #
76
+ # @api private
77
+ #
78
+ def reporter
79
+ Mutant::Reporter::CLI.new($stdout)
80
+ end
81
+ memoize :reporter
82
+
83
+ private
84
+
85
+ OPTIONS = {
86
+ '--code' => [:add_filter, Mutation::Filter::Code ],
87
+ '-I' => [:add_load_path ],
88
+ '--include' => [:add_load_path ],
89
+ '-r' => [:require_library ],
90
+ '--require' => [:require_library ],
91
+ #'--killer-fork' => [:enable_killer_fork ],
92
+ '--rspec-unit' => [:set_strategy, Strategy::Rspec::Unit ],
93
+ '--rspec-full' => [:set_strategy, Strategy::Rspec::Full ],
94
+ '--rspec-dm2' => [:set_strategy, Strategy::Rspec::DM2 ],
95
+ '--static-fail' => [:set_strategy, Strategy::Static::Fail ],
96
+ '--static-success' => [:set_strategy, Strategy::Static::Success ]
97
+ }.freeze
98
+
99
+ OPTION_PATTERN = %r(\A-(?:-)?[a-zA-Z0-9\-]+\z).freeze
100
+
101
+ # Return selected killer
102
+ #
103
+ # @return [Killer]
104
+ #
105
+ # @api private
106
+ #
107
+ def selected_killer
108
+ unless @rspec
109
+ raise Error, "Only rspec is supported currently use --rspec switch"
110
+ end
111
+
112
+ Mutant::Killer::Rspec
113
+ end
114
+ memoize :selected_killer
115
+
116
+ # Return option for argument with index
117
+ #
118
+ # @param [Fixnum] index
119
+ #
120
+ # @return [String]
121
+ #
122
+ # @api private
123
+ #
124
+ def option(index)
125
+ @arguments.fetch(index+1)
126
+ end
127
+
128
+ # Initialize CLI
129
+ #
130
+ # @param [Array<String>] arguments
131
+ #
132
+ # @return [undefined]
133
+ #
134
+ # @api private
135
+ #
136
+ def initialize(arguments)
137
+ @filters, @matchers = [], []
138
+
139
+ @arguments, @index = arguments, 0
140
+
141
+ while @index < @arguments.length
142
+ dispatch
143
+ end
144
+
145
+ strategy
146
+ matcher
147
+ end
148
+
149
+ # Return current argument
150
+ #
151
+ # @return [String]
152
+ #
153
+ # @api private
154
+ #
155
+ def current_argument
156
+ @arguments.fetch(@index)
157
+ end
158
+
159
+ # Return current option value
160
+ #
161
+ # @return [String]
162
+ #
163
+ # @raise [CLI::Error]
164
+ # raises error when option is missing
165
+ #
166
+ # @api private
167
+ #
168
+ def current_option_value
169
+ @arguments.fetch(@index+1)
170
+ rescue IndexError
171
+ raise Error, "#{current_argument.inspect} is missing an argument"
172
+ end
173
+
174
+ # Process current argument
175
+ #
176
+ # @return [undefined]
177
+ #
178
+ # @api private
179
+ #
180
+ def dispatch
181
+ if OPTION_PATTERN =~ current_argument
182
+ dispatch_option
183
+ else
184
+ dispatch_matcher
185
+ end
186
+ end
187
+
188
+ # Move processed argument by amount
189
+ #
190
+ # @param [Fixnum] amount
191
+ # the amount of arguments to be consumed
192
+ #
193
+ # @return [undefined]
194
+ #
195
+ # @api private
196
+ #
197
+ def consume(amount)
198
+ @index += amount
199
+ end
200
+
201
+ # Process matcher argument
202
+ #
203
+ # @return [undefined]
204
+ #
205
+ # @api private
206
+ #
207
+ def dispatch_matcher
208
+ argument = current_argument
209
+ matcher = Mutant::Matcher.from_string(argument)
210
+
211
+ unless matcher
212
+ raise Error, "Invalid matcher syntax: #{argument.inspect}"
213
+ end
214
+
215
+ @matchers << matcher
216
+
217
+ consume(1)
218
+ end
219
+
220
+ # Process option argument
221
+ #
222
+ # @return [Undefined]
223
+ #
224
+ # @api private
225
+ #
226
+ def dispatch_option
227
+ argument = current_argument
228
+ arguments = *OPTIONS.fetch(argument) do
229
+ raise Error, "Unknown option: #{argument.inspect}"
230
+ end
231
+ send(*arguments)
232
+ end
233
+
234
+ # Add mutation filter
235
+ #
236
+ # @param [Class<Mutant::Filter>]
237
+ #
238
+ # @return [undefined]
239
+ #
240
+ # @api private
241
+ #
242
+ def add_filter(klass)
243
+ @filters << klass.new(current_option_value)
244
+ consume(2)
245
+ end
246
+
247
+ # Add load path
248
+ #
249
+ # @api private
250
+ #
251
+ # @return [undefined]
252
+ #
253
+ def add_load_path
254
+ $LOAD_PATH << current_option_value
255
+ consume(2)
256
+ end
257
+
258
+ # Enable rspec
259
+ #
260
+ # @api private
261
+ #
262
+ # @return [self]
263
+ #
264
+ # @api private
265
+ #
266
+ def enable_rspec
267
+ consume(1)
268
+ @rspec = true
269
+ end
270
+
271
+ # Enable killer forking
272
+ #
273
+ # @api private
274
+ #
275
+ # @return [self]
276
+ #
277
+ # @api private
278
+ #
279
+ def enable_killer_fork
280
+ consume(1)
281
+ @forking = true
282
+ end
283
+
284
+ # Set strategy
285
+ #
286
+ # @param [Strategy]
287
+ #
288
+ # @api private
289
+ #
290
+ # @return [undefined]
291
+ #
292
+ def set_strategy(strategy)
293
+ consume(1)
294
+ @strategy = strategy
295
+ end
296
+
297
+ # Require library
298
+ #
299
+ # @api private
300
+ #
301
+ # @return [undefined]
302
+ #
303
+ def require_library
304
+ require(current_option_value)
305
+ consume(2)
306
+ end
307
+
308
+ end
309
+ end
@@ -0,0 +1,61 @@
1
+ module Mutant
2
+ # Class to colorize strings
3
+ class Color
4
+ include Adamantium::Flat
5
+
6
+ # Initialize color object
7
+ #
8
+ # @param [Fixnum] code
9
+ #
10
+ # @return [undefined]
11
+ #
12
+ # @api private
13
+ #
14
+ def initialize(code)
15
+ @code = code
16
+ end
17
+
18
+ # Format text with color
19
+ #
20
+ # @param [String] text
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ #
26
+ def format(text)
27
+ "\e[#{@code}m#{text}\e[0m"
28
+ end
29
+
30
+ NONE = Class.new(self) do
31
+ # Format null color
32
+ #
33
+ # @param [String] text
34
+ #
35
+ # @return [String]
36
+ # returns the argument string
37
+ #
38
+ # @api private
39
+ #
40
+ def format(text)
41
+ text
42
+ end
43
+
44
+ private
45
+
46
+ # Initialize null color
47
+ #
48
+ # @return [undefined]
49
+ #
50
+ # @api private
51
+ #
52
+ def initialize(*)
53
+ end
54
+
55
+ end.new.freeze
56
+
57
+ RED = Color.new(31)
58
+ GREEN = Color.new(32)
59
+ BLUE = Color.new(34)
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ module Mutant
2
+ # An abstract context where mutations can be appied to.
3
+ class Context
4
+ include Adamantium::Flat, AbstractType
5
+
6
+ # Return root ast node
7
+ #
8
+ # @return [Rubinis::AST::Script]
9
+ #
10
+ # @api private
11
+ #
12
+ abstract_method :root
13
+
14
+ # Return source path
15
+ #
16
+ # @return [String]
17
+ #
18
+ # @api private
19
+ #
20
+ attr_reader :source_path
21
+
22
+ private
23
+
24
+ # Initialize context
25
+ #
26
+ # @param [String] source_path
27
+ #
28
+ # @return [undefined]
29
+ #
30
+ # @api private
31
+ #
32
+ def initialize(source_path)
33
+ @source_path = source_path
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,138 @@
1
+ module Mutant
2
+ class Context
3
+ # Scope context for mutation (Class or Module)
4
+ class Scope < self
5
+ include Adamantium::Flat, AbstractType, Equalizer.new(:scope, :source_path)
6
+
7
+ # Class context for mutation
8
+ class Class < self
9
+ SCOPE_CLASS = Rubinius::AST::ClassScope
10
+ KEYWORD = 'class'.freeze
11
+ end
12
+
13
+ # Module context for mutation
14
+ class Module < self
15
+ SCOPE_CLASS = Rubinius::AST::ModuleScope
16
+ KEYWORD = 'module'.freeze
17
+ end
18
+
19
+ TABLE = {
20
+ ::Module => Module,
21
+ ::Class => Class
22
+ }.freeze
23
+
24
+ # Build scope context from class or module
25
+ #
26
+ # @param [String] source_path
27
+ #
28
+ # @param [::Class|::Module] scope
29
+ #
30
+ # @return [Context::Scope]
31
+ #
32
+ # @api private
33
+ #
34
+ def self.build(scope, source_path)
35
+ scope_class = scope.class
36
+ klass = TABLE.fetch(scope_class) do
37
+ raise ArgumentError, "Can only build mutation scope from class or module got: #{scope_class.inspect}"
38
+ end.new(scope, source_path)
39
+ end
40
+
41
+ # Return AST wrapping mutated node
42
+ #
43
+ # @return [Rubinius::AST::Script]
44
+ #
45
+ # @api private
46
+ #
47
+ def root(node)
48
+ root = root_ast
49
+ block = Rubinius::AST::Block.new(1, [node])
50
+ root.body = scope_class.new(1, root.name, block)
51
+ root
52
+ end
53
+
54
+ # Return unqualified name of scope
55
+ #
56
+ # @return [String]
57
+ #
58
+ # @api private
59
+ #
60
+ def unqualified_name
61
+ name_nesting.last
62
+ end
63
+
64
+ # Return scope wrapped by context
65
+ #
66
+ # @return [::Module|::Class]
67
+ #
68
+ # @api private
69
+ #
70
+ attr_reader :scope
71
+
72
+ private
73
+
74
+ # Initialize object
75
+ #
76
+ # @param [Object] scope
77
+ # @param [String] source_path
78
+ #
79
+ # @api private
80
+ #
81
+ def initialize(scope, source_path)
82
+ super(source_path)
83
+ @scope = scope
84
+ end
85
+
86
+ # Return scope AST class
87
+ #
88
+ # @return [Rubinius::AST::Node]
89
+ #
90
+ # @api private
91
+ #
92
+ def scope_class
93
+ self.class::SCOPE_CLASS
94
+ end
95
+
96
+ # Return keyword
97
+ #
98
+ # @return [Rubinius::AST::Node]
99
+ #
100
+ # @api private
101
+ #
102
+ def keyword
103
+ self.class::KEYWORD
104
+ end
105
+
106
+ # Return new root ast
107
+ #
108
+ # @return [Rubinius::AST::Node]
109
+ #
110
+ # @api private
111
+ #
112
+ def root_ast
113
+ "#{keyword} #{qualified_name}; end".to_ast
114
+ end
115
+
116
+ # Return qualified name of scope
117
+ #
118
+ # @return [String]
119
+ #
120
+ # @api private
121
+ #
122
+ def qualified_name
123
+ scope.name
124
+ end
125
+
126
+ # Return nesting of names of scope
127
+ #
128
+ # @return [Array<String>]
129
+ #
130
+ # @api private
131
+ #
132
+ def name_nesting
133
+ scope.name.split('::')
134
+ end
135
+ memoize :name_nesting
136
+ end
137
+ end
138
+ end