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,75 @@
1
+ module Mutant
2
+ class Mutation
3
+ # Abstract filter for mutations
4
+ class Filter
5
+ include Adamantium::Flat, AbstractType
6
+ extend DescendantsTracker
7
+
8
+ # Check for match
9
+ #
10
+ # @param [Mutation] mutation
11
+ #
12
+ # @return [true]
13
+ # returns true if mutation is matched by filter
14
+ #
15
+ # @return [false]
16
+ # returns false otherwise
17
+ #
18
+ # @api private
19
+ #
20
+ abstract_method :match?
21
+
22
+ # Build filter from string
23
+ #
24
+ # @param [String] notation
25
+ #
26
+ # @return [Filter]
27
+ # returns filter when can be buld from string
28
+ #
29
+ # @return [nil]
30
+ # returns nil otherwise
31
+ #
32
+ # @api private
33
+ #
34
+ def self.build(notation)
35
+ descendants.each do |descendant|
36
+ filter = descendant.handle(notation)
37
+ return filter if filter
38
+ end
39
+
40
+ nil
41
+ end
42
+
43
+ # Return filter for handle
44
+ #
45
+ # @param [String] notation
46
+ #
47
+ # @return [nil]
48
+ # returns nil
49
+ #
50
+ # @api private
51
+ #
52
+ def self.handle(notation)
53
+ nil
54
+ end
55
+
56
+ # Mutation filter matching all mutations
57
+ Mutant.define_singleton_subclass('ALL', self) do
58
+
59
+ # Test for match
60
+ #
61
+ # @pram [Mutation] mutation
62
+ #
63
+ # @return [true]
64
+ # returns true
65
+ #
66
+ # @api private
67
+ #
68
+ def match?(mutation)
69
+ true
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,68 @@
1
+ module Mutant
2
+ class Mutation
3
+ class Filter
4
+ # Mutation filter that filters on mutation codes
5
+ class Code < self
6
+ include Equalizer.new(:code)
7
+
8
+ # Test for match
9
+ #
10
+ # @param [Mutation] mutation
11
+ #
12
+ # @return [true]
13
+ # returns true if mutation code matches filter code
14
+ #
15
+ # @return [false]
16
+ # returns false otherwise
17
+ #
18
+ # @api private
19
+ #
20
+ def match?(mutation)
21
+ mutation.code.eql?(code)
22
+ end
23
+
24
+ PATTERN = %r(\Acode:([a-f0-9]{1,6})\z).freeze
25
+
26
+ # Test if class handles string
27
+ #
28
+ # @param [String] notation
29
+ #
30
+ # @return [Filter]
31
+ # return code filter instance if notation matches pattern
32
+ #
33
+ # @return [nil]
34
+ # returns nil otherwise
35
+ #
36
+ # @api private
37
+ #
38
+ def self.handle(notation)
39
+ match = PATTERN.match(notation)
40
+ return unless match
41
+ new(match[1])
42
+ end
43
+
44
+ # Return code
45
+ #
46
+ # @return [String]
47
+ #
48
+ # @api private
49
+ #
50
+ attr_reader :code
51
+
52
+ private
53
+
54
+ # Initialize code filter
55
+ #
56
+ # @param [String] code
57
+ #
58
+ # @return [undefined]
59
+ #
60
+ # @api private
61
+ #
62
+ def initialize(code)
63
+ @code = code
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ module Mutant
2
+ class Mutation
3
+ class Filter
4
+ # Mutaiton filter filtering in regexp match on mutation identification
5
+ class Regexp < self
6
+
7
+ # Test for match
8
+ #
9
+ # @param [Mutation] mutation
10
+ #
11
+ # @return [true]
12
+ # returns true if mutation identification is matched by regexp
13
+ #
14
+ # @return [false]
15
+ # returns false otherwise
16
+ #
17
+ # @api private
18
+ #
19
+ def match?(mutation)
20
+ !!@regexp.match =~ mutation.identification
21
+ end
22
+
23
+ private
24
+
25
+ # Initialize regexp filter
26
+ #
27
+ # @param [Regexp] regexp
28
+ #
29
+ # @return [undefined]
30
+ #
31
+ # @api private
32
+ #
33
+ def initialize(regexp)
34
+ @regexp = regexp
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ module Mutant
2
+ class Mutation
3
+ class Filter
4
+ class Whitelist < self
5
+ include Adamantium::Flat, Equalizer.new(:whitelist)
6
+
7
+ # Test for match
8
+ #
9
+ # @param [Mutation]
10
+ #
11
+ # @return [true]
12
+ # returns true if mutation matches whitelist
13
+ #
14
+ # @return [false]
15
+ # returns false otherwise
16
+ #
17
+ # @api private
18
+ #
19
+ def match?(mutation)
20
+ @whitelist.any? { |filter| filter.match?(mutation) }
21
+ end
22
+
23
+ # Return whitelist
24
+ #
25
+ # @return [Enumerable<Filter>]
26
+ #
27
+ # @api private
28
+ #
29
+ attr_reader :whitelist
30
+
31
+ private
32
+
33
+ # Initalize white list
34
+ #
35
+ # @param [Enumerable<Filter>] whitelist
36
+ #
37
+ # @return [undefined]
38
+ #
39
+ # @api private
40
+ #
41
+ def initialize(whitelist)
42
+ @whitelist = whitelist
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,49 +1,153 @@
1
1
  module Mutant
2
+ # Generator for mutations
2
3
  class Mutator
3
- COMPILER = Rubinius::Compiler.new(:bytecode, :compiled_method)
4
+ include Adamantium::Flat, AbstractType
4
5
 
5
- def initialize(mutatee)
6
- @mutatee = mutatee
6
+ # Run mutator on input
7
+ #
8
+ # @param [Object] input
9
+ # @param [#call] block
10
+ #
11
+ # @return [self]
12
+ #
13
+ # @api private
14
+ #
15
+ def self.each(node, &block)
16
+ return to_enum(__method__, node) unless block_given?
17
+ Registry.lookup(node.class).new(node, block)
18
+
19
+ self
7
20
  end
8
21
 
9
- def mutate
10
- @mutatee.clean
11
- @mutatee.set_mutations
22
+ # Register node class handler
23
+ #
24
+ # @param [Class:Rubinius::AST::Node] node_class
25
+ #
26
+ # @return [undefined]
27
+ #
28
+ # @api private
29
+ #
30
+ def self.handle(node_class)
31
+ Registry.register(node_class,self)
32
+ end
33
+ private_class_method :handle
12
34
 
13
- if @mutatee.mutations.size.zero?
14
- Reporter.no_mutations(@mutatee)
15
- return
16
- else
17
- Reporter.method_loaded(@mutatee)
18
- end
35
+ # Return input
36
+ #
37
+ # @return [Object]
38
+ #
39
+ # @api private
40
+ #
41
+ attr_reader :input
19
42
 
20
- @mutatee.mutations.each do |mutation|
21
- @mutatee.body.array = mutation.mutate && mutation.array
22
- Reporter.mutating(mutation)
23
- # setup the compiler
24
- COMPILER.generator.input(root)
25
- # compile the script and replace the original method
26
- # with the mutated method
27
- Rubinius.run_script(compiled_method)
28
- end
43
+ private
44
+
45
+ # Initialize object
46
+ #
47
+ # @param [Object] input
48
+ # @param [#call(node)] block
49
+ #
50
+ # @return [undefined]
51
+ #
52
+ # @api private
53
+ #
54
+ def initialize(input, block)
55
+ @input, @block = Helper.deep_clone(input), block
56
+ IceNine.deep_freeze(@input)
57
+ dispatch
29
58
  end
30
59
 
31
- def block
32
- Rubinius::AST::Block.new(1, [ @mutatee.ast ])
60
+ # Test if generated object is different from input
61
+ #
62
+ # @param [Object] object
63
+ #
64
+ # @return [true]
65
+ # if generated object is different
66
+ #
67
+ # @return [false]
68
+ #
69
+ # @api private
70
+ #
71
+ def new?(object)
72
+ input != object
33
73
  end
34
74
 
35
- def class_ast
36
- Rubinius::AST::Class.new(1, @mutatee.class_name.to_sym, nil, block)
75
+ # Dispatch node generations
76
+ #
77
+ # @return [undefined]
78
+ #
79
+ # @api private
80
+ #
81
+ abstract_method :dispatch
82
+
83
+ # Emit generated mutation if object is not equivalent to input
84
+ #
85
+ # @param [Object] object
86
+ #
87
+ # @return [undefined]
88
+ #
89
+ # @api private
90
+ #
91
+ def emit(object)
92
+ return unless new?(object)
93
+
94
+ emit!(object)
37
95
  end
38
96
 
39
- def root
40
- Rubinius::AST::Script.new(class_ast).tap do |script|
41
- script.file = @mutatee.rbx_method.source_file
97
+ # Maximum amount of tries to generate a new object
98
+ MAX_TRIES = 3
99
+
100
+ # Call block until it generates a mutation
101
+ #
102
+ # The primary use of this method is to give the random generated object
103
+ # a nice interface for retring generation when generation accidentally generated the
104
+ # input
105
+ #
106
+ # @yield
107
+ # Execute block until object is generated where new?(object) returns true
108
+ #
109
+ # @return [self]
110
+ #
111
+ # @raise [RuntimeError]
112
+ # raises RuntimeError in case no new ast node can be generated after MAX_TRIES.
113
+ #
114
+ # @api private
115
+ #
116
+ def emit_new
117
+ MAX_TRIES.times do
118
+ object = yield
119
+
120
+ if new?(object)
121
+ emit!(object)
122
+ return
123
+ end
42
124
  end
125
+
126
+ raise "New AST could not be generated after #{MAX_TRIES} attempts"
43
127
  end
44
128
 
45
- def compiled_method
46
- COMPILER.run.create_script.compiled_method
129
+ # Call block with node
130
+ #
131
+ # @param [Rubinius::AST::Node] node
132
+ #
133
+ # @return [self]
134
+ #
135
+ # @api private
136
+ #
137
+ def emit!(node)
138
+ @block.call(node)
139
+ self
47
140
  end
141
+
142
+ # Shortcut to create a new unfrozen duplicate of input
143
+ #
144
+ # @return [Object]
145
+ #
146
+ # @api private
147
+ #
148
+ def dup_input
149
+ input.dup
150
+ end
151
+
48
152
  end
49
153
  end
@@ -0,0 +1,163 @@
1
+ module Mutant
2
+ # Generator for mutations
3
+ class Mutator
4
+ # Abstract base class for node mutators
5
+ class Node < self
6
+ include AbstractType
7
+
8
+ private
9
+
10
+ alias_method :node, :input
11
+ alias_method :dup_node, :dup_input
12
+
13
+ # Return source of input node
14
+ #
15
+ # @return [String]
16
+ #
17
+ # @api private
18
+ #
19
+ def source
20
+ ToSource.to_source(node)
21
+ end
22
+ memoize :source
23
+
24
+ # Test if generated node is new
25
+ #
26
+ # @return [true]
27
+ # if generated node is different from input
28
+ #
29
+ # @return [false]
30
+ # otherwise
31
+ #
32
+ # @api private
33
+ #
34
+ def new?(node)
35
+ source != ToSource.to_source(node)
36
+ end
37
+
38
+ # Emit a new AST node
39
+ #
40
+ # @param [Rubinis::AST::Node:Class] node_class
41
+ #
42
+ # @return [Rubinius::AST::Node]
43
+ #
44
+ # @api private
45
+ #
46
+ def emit_node(node_class, *arguments)
47
+ emit(new(node_class, *arguments))
48
+ end
49
+
50
+ # Create a new AST node with same class as wrapped node
51
+ #
52
+ # @return [Rubinius::AST::Node]
53
+ #
54
+ # @api private
55
+ #
56
+ def new_self(*arguments)
57
+ new(node.class, *arguments)
58
+ end
59
+
60
+ # Create a new AST node with Rubnius::AST::NilLiteral class
61
+ #
62
+ # @return [Rubinius::AST::Node]
63
+ #
64
+ # @api private
65
+ #
66
+ def new_nil
67
+ new(Rubinius::AST::NilLiteral)
68
+ end
69
+
70
+ # Create a new AST node with the same line as wrapped node
71
+ #
72
+ # @param [Class:Rubinius::AST::Node] node_class
73
+ #
74
+ # @return [Rubinius::AST::Node]
75
+ #
76
+ # @api private
77
+ #
78
+ def new(node_class, *arguments)
79
+ node_class.new(node.line, *arguments)
80
+ end
81
+
82
+ # Emit a new AST node with same class as wrapped node
83
+ #
84
+ # @return [undefined]
85
+ #
86
+ # @api private
87
+ #
88
+ def emit_self(*arguments)
89
+ emit(new_self(*arguments))
90
+ end
91
+
92
+ # Emit a new node with wrapping class for each entry in values
93
+ #
94
+ # @param [Array] values
95
+ #
96
+ # @return [undefined]
97
+ #
98
+ # @api private
99
+ #
100
+ def emit_values(values)
101
+ values.each do |value|
102
+ emit_self(value)
103
+ end
104
+ end
105
+
106
+ # Emit body mutations
107
+ #
108
+ # @param [Symbol] name
109
+ #
110
+ # @return [undefined]
111
+ #
112
+ # @api private
113
+ #
114
+ def emit_attribute_mutations(name)
115
+ body = node.public_send(name)
116
+
117
+ Mutator.each(body) do |mutation|
118
+ dup = dup_node
119
+ dup.public_send(:"#{name}=", mutation)
120
+ emit(dup)
121
+ end
122
+ end
123
+
124
+ # Emit a new AST node with NilLiteral class
125
+ #
126
+ # @return [Rubinius::AST::NilLiteral]
127
+ #
128
+ # @api private
129
+ #
130
+ def emit_nil
131
+ emit(new_nil)
132
+ end
133
+
134
+ # Return AST representing send
135
+ #
136
+ # @param [Rubinius::AST::Node] receiver
137
+ # @param [Symbol] name
138
+ # @param [Rubinius::AST::Node] arguments
139
+ #
140
+ # @return [Rubnius::AST::SendWithArguments]
141
+ #
142
+ # @api private
143
+ #
144
+ def new_send(receiver, name, arguments=nil)
145
+ if arguments
146
+ new(Rubinius::AST::SendWithArguments, receiver, name, arguments)
147
+ else
148
+ new(Rubinius::AST::Send, receiver, name)
149
+ end
150
+ end
151
+
152
+ # Return duplicated (unfrozen) node each call
153
+ #
154
+ # @return [Rubinius::AST::Node]
155
+ #
156
+ # @api private
157
+ #
158
+ def dup_node
159
+ node.dup
160
+ end
161
+ end
162
+ end
163
+ end