mutant 0.8.10 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -4
  3. data/Changelog.md +8 -0
  4. data/README.md +112 -43
  5. data/Rakefile +2 -16
  6. data/circle.yml +1 -1
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/reek.yml +3 -4
  10. data/config/rubocop.yml +53 -16
  11. data/lib/mutant.rb +27 -6
  12. data/lib/mutant/ast/meta/const.rb +2 -0
  13. data/lib/mutant/ast/meta/optarg.rb +2 -0
  14. data/lib/mutant/ast/meta/resbody.rb +2 -0
  15. data/lib/mutant/ast/meta/restarg.rb +2 -0
  16. data/lib/mutant/ast/meta/send.rb +4 -0
  17. data/lib/mutant/ast/meta/symbol.rb +2 -0
  18. data/lib/mutant/ast/named_children.rb +14 -4
  19. data/lib/mutant/ast/nodes.rb +3 -1
  20. data/lib/mutant/ast/regexp.rb +53 -0
  21. data/lib/mutant/ast/regexp/transformer.rb +185 -0
  22. data/lib/mutant/ast/regexp/transformer/alternative.rb +39 -0
  23. data/lib/mutant/ast/regexp/transformer/character_set.rb +46 -0
  24. data/lib/mutant/ast/regexp/transformer/direct.rb +99 -0
  25. data/lib/mutant/ast/regexp/transformer/options_group.rb +66 -0
  26. data/lib/mutant/ast/regexp/transformer/quantifier.rb +112 -0
  27. data/lib/mutant/ast/regexp/transformer/recursive.rb +50 -0
  28. data/lib/mutant/ast/regexp/transformer/root.rb +29 -0
  29. data/lib/mutant/ast/regexp/transformer/text.rb +55 -0
  30. data/lib/mutant/ast/types.rb +92 -5
  31. data/lib/mutant/cli.rb +2 -14
  32. data/lib/mutant/color.rb +1 -1
  33. data/lib/mutant/config.rb +1 -3
  34. data/lib/mutant/expression/methods.rb +1 -1
  35. data/lib/mutant/expression/namespace.rb +2 -2
  36. data/lib/mutant/expression/parser.rb +1 -1
  37. data/lib/mutant/integration.rb +10 -28
  38. data/lib/mutant/isolation.rb +9 -60
  39. data/lib/mutant/isolation/fork.rb +72 -0
  40. data/lib/mutant/isolation/none.rb +25 -0
  41. data/lib/mutant/matcher/config.rb +2 -0
  42. data/lib/mutant/matcher/method/singleton.rb +5 -4
  43. data/lib/mutant/meta.rb +11 -4
  44. data/lib/mutant/meta/example.rb +2 -116
  45. data/lib/mutant/meta/example/dsl.rb +22 -19
  46. data/lib/mutant/meta/example/verification.rb +86 -0
  47. data/lib/mutant/mutator.rb +22 -49
  48. data/lib/mutant/mutator/node.rb +15 -19
  49. data/lib/mutant/mutator/node/and_asgn.rb +1 -1
  50. data/lib/mutant/mutator/node/argument.rb +10 -5
  51. data/lib/mutant/mutator/node/arguments.rb +5 -9
  52. data/lib/mutant/mutator/node/begin.rb +4 -17
  53. data/lib/mutant/mutator/node/block.rb +1 -1
  54. data/lib/mutant/mutator/node/break.rb +1 -1
  55. data/lib/mutant/mutator/node/class.rb +21 -0
  56. data/lib/mutant/mutator/node/conditional_loop.rb +1 -1
  57. data/lib/mutant/mutator/node/define.rb +1 -1
  58. data/lib/mutant/mutator/node/defined.rb +1 -1
  59. data/lib/mutant/mutator/node/dstr.rb +1 -1
  60. data/lib/mutant/mutator/node/dsym.rb +1 -1
  61. data/lib/mutant/mutator/node/generic.rb +3 -3
  62. data/lib/mutant/mutator/node/kwbegin.rb +1 -1
  63. data/lib/mutant/mutator/node/literal.rb +9 -0
  64. data/lib/mutant/mutator/node/literal/boolean.rb +1 -1
  65. data/lib/mutant/mutator/node/literal/fixnum.rb +2 -2
  66. data/lib/mutant/mutator/node/literal/float.rb +4 -6
  67. data/lib/mutant/mutator/node/literal/hash.rb +1 -1
  68. data/lib/mutant/mutator/node/literal/nil.rb +1 -1
  69. data/lib/mutant/mutator/node/literal/range.rb +2 -19
  70. data/lib/mutant/mutator/node/literal/regex.rb +43 -3
  71. data/lib/mutant/mutator/node/literal/string.rb +1 -1
  72. data/lib/mutant/mutator/node/literal/symbol.rb +2 -4
  73. data/lib/mutant/mutator/node/masgn.rb +1 -1
  74. data/lib/mutant/mutator/node/match_current_line.rb +1 -1
  75. data/lib/mutant/mutator/node/mlhs.rb +2 -3
  76. data/lib/mutant/mutator/node/named_value/access.rb +2 -2
  77. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +4 -3
  78. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +4 -4
  79. data/lib/mutant/mutator/node/next.rb +1 -1
  80. data/lib/mutant/mutator/node/nthref.rb +1 -1
  81. data/lib/mutant/mutator/node/or_asgn.rb +1 -1
  82. data/lib/mutant/mutator/node/regexp.rb +44 -0
  83. data/lib/mutant/mutator/node/regopt.rb +31 -0
  84. data/lib/mutant/mutator/node/resbody.rb +1 -1
  85. data/lib/mutant/mutator/node/rescue.rb +1 -3
  86. data/lib/mutant/mutator/node/return.rb +1 -1
  87. data/lib/mutant/mutator/node/send.rb +43 -3
  88. data/lib/mutant/mutator/node/send/attribute_assignment.rb +4 -1
  89. data/lib/mutant/mutator/node/send/conditional.rb +23 -0
  90. data/lib/mutant/mutator/node/send/index.rb +1 -1
  91. data/lib/mutant/mutator/node/splat.rb +1 -1
  92. data/lib/mutant/mutator/node/when.rb +1 -1
  93. data/lib/mutant/mutator/node/yield.rb +1 -1
  94. data/lib/mutant/mutator/util.rb +0 -30
  95. data/lib/mutant/mutator/util/array.rb +4 -16
  96. data/lib/mutant/parallel.rb +1 -1
  97. data/lib/mutant/parallel/worker.rb +1 -1
  98. data/lib/mutant/registry.rb +44 -0
  99. data/lib/mutant/reporter/cli/format.rb +2 -0
  100. data/lib/mutant/reporter/cli/printer.rb +2 -2
  101. data/lib/mutant/reporter/cli/printer/config.rb +0 -1
  102. data/lib/mutant/reporter/cli/printer/env_progress.rb +1 -11
  103. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +1 -1
  104. data/lib/mutant/reporter/cli/printer/mutation_result.rb +2 -0
  105. data/lib/mutant/reporter/cli/tput.rb +1 -1
  106. data/lib/mutant/reporter/sequence.rb +3 -0
  107. data/lib/mutant/require_highjack.rb +6 -2
  108. data/lib/mutant/result.rb +1 -1
  109. data/lib/mutant/subject.rb +5 -5
  110. data/lib/mutant/subject/method/instance.rb +1 -2
  111. data/lib/mutant/util.rb +18 -0
  112. data/lib/mutant/version.rb +1 -1
  113. data/lib/mutant/zombifier.rb +5 -3
  114. data/meta/and.rb +1 -1
  115. data/meta/and_asgn.rb +1 -1
  116. data/meta/array.rb +2 -2
  117. data/meta/begin.rb +2 -2
  118. data/meta/block.rb +7 -7
  119. data/meta/block_pass.rb +1 -1
  120. data/meta/blockarg.rb +1 -1
  121. data/meta/break.rb +1 -1
  122. data/meta/case.rb +2 -2
  123. data/meta/casgn.rb +11 -2
  124. data/meta/cbase.rb +1 -1
  125. data/meta/class.rb +10 -0
  126. data/meta/const.rb +9 -1
  127. data/meta/csend.rb +8 -0
  128. data/meta/cvar.rb +1 -1
  129. data/meta/cvasgn.rb +1 -1
  130. data/meta/date.rb +4 -4
  131. data/meta/def.rb +14 -14
  132. data/meta/defined.rb +1 -1
  133. data/meta/dstr.rb +1 -1
  134. data/meta/dsym.rb +1 -1
  135. data/meta/ensure.rb +1 -1
  136. data/meta/false.rb +1 -1
  137. data/meta/float.rb +3 -3
  138. data/meta/gvar.rb +1 -1
  139. data/meta/gvasgn.rb +1 -1
  140. data/meta/hash.rb +1 -1
  141. data/meta/if.rb +17 -3
  142. data/meta/int.rb +1 -1
  143. data/meta/ivar.rb +1 -1
  144. data/meta/ivasgn.rb +14 -1
  145. data/meta/kwarg.rb +8 -0
  146. data/meta/kwbegin.rb +1 -1
  147. data/meta/kwoptarg.rb +11 -0
  148. data/meta/lvar.rb +1 -1
  149. data/meta/lvasgn.rb +1 -1
  150. data/meta/masgn.rb +1 -1
  151. data/meta/match_current_line.rb +2 -2
  152. data/meta/next.rb +1 -1
  153. data/meta/nil.rb +1 -1
  154. data/meta/nthref.rb +5 -5
  155. data/meta/op_assgn.rb +1 -1
  156. data/meta/or.rb +1 -1
  157. data/meta/or_asgn.rb +5 -5
  158. data/meta/range.rb +2 -8
  159. data/meta/redo.rb +1 -1
  160. data/meta/regexp.rb +106 -0
  161. data/meta/regexp/regexp_bol_anchor.rb +13 -0
  162. data/meta/regexp/regexp_bos_anchor.rb +26 -0
  163. data/meta/regexp/regexp_root_expression.rb +13 -0
  164. data/meta/regopt.rb +8 -0
  165. data/meta/rescue.rb +49 -4
  166. data/meta/restarg.rb +6 -9
  167. data/meta/return.rb +2 -2
  168. data/meta/self.rb +1 -1
  169. data/meta/send.rb +228 -55
  170. data/meta/str.rb +1 -1
  171. data/meta/super.rb +3 -3
  172. data/meta/{symbol.rb → sym.rb} +1 -1
  173. data/meta/true.rb +1 -1
  174. data/meta/until.rb +1 -1
  175. data/meta/while.rb +2 -2
  176. data/meta/yield.rb +1 -1
  177. data/mutant-rspec.gemspec +2 -2
  178. data/mutant.gemspec +6 -5
  179. data/spec/integration/mutant/isolation/fork_spec.rb +8 -0
  180. data/spec/integration/mutant/rspec_spec.rb +1 -1
  181. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -2
  182. data/spec/integrations.yml +93 -24
  183. data/spec/spec_helper.rb +12 -7
  184. data/spec/support/compress_helper.rb +1 -1
  185. data/spec/support/corpus.rb +115 -50
  186. data/spec/support/fake_actor.rb +5 -5
  187. data/spec/support/file_system.rb +1 -1
  188. data/spec/support/ice_nine_config.rb +3 -3
  189. data/spec/support/ruby_vm.rb +11 -12
  190. data/spec/support/shared_context.rb +22 -13
  191. data/spec/support/test_app.rb +1 -1
  192. data/spec/support/warning.rb +64 -0
  193. data/spec/support/warnings.yml +4 -0
  194. data/spec/support/xspec.rb +177 -0
  195. data/spec/unit/mutant/actor/env_spec.rb +2 -2
  196. data/spec/unit/mutant/actor/sender_spec.rb +1 -1
  197. data/spec/unit/mutant/ast/meta/send_spec.rb +1 -1
  198. data/spec/unit/mutant/ast/named_children_spec.rb +26 -0
  199. data/spec/unit/mutant/ast/regexp/parse_spec.rb +7 -0
  200. data/spec/unit/mutant/ast/regexp/supported_predicate_spec.rb +14 -0
  201. data/spec/unit/mutant/ast/regexp/transformer/lookup_table/table_spec.rb +19 -0
  202. data/spec/unit/mutant/ast/regexp/transformer/lookup_table_spec.rb +33 -0
  203. data/spec/unit/mutant/ast/regexp/transformer_spec.rb +19 -0
  204. data/spec/unit/mutant/ast/regexp_spec.rb +617 -0
  205. data/spec/unit/mutant/cli_spec.rb +7 -45
  206. data/spec/unit/mutant/context_spec.rb +4 -7
  207. data/spec/unit/mutant/env/{boostrap_spec.rb → bootstrap_spec.rb} +2 -2
  208. data/spec/unit/mutant/env_spec.rb +13 -16
  209. data/spec/unit/mutant/expression/namespace/{flat_spec.rb → exact_spec.rb} +0 -0
  210. data/spec/unit/mutant/integration/rspec_spec.rb +2 -2
  211. data/spec/unit/mutant/integration_spec.rb +14 -0
  212. data/spec/unit/mutant/isolation/fork_spec.rb +155 -0
  213. data/spec/unit/mutant/isolation/none_spec.rb +16 -0
  214. data/spec/unit/mutant/loader_spec.rb +1 -1
  215. data/spec/unit/mutant/matcher/methods/instance_spec.rb +2 -4
  216. data/spec/unit/mutant/meta/example/dsl_spec.rb +106 -0
  217. data/spec/unit/mutant/meta/example/verification_spec.rb +120 -0
  218. data/spec/unit/mutant/meta/example_spec.rb +32 -0
  219. data/spec/unit/mutant/mutator/node_spec.rb +37 -4
  220. data/spec/unit/mutant/mutator_spec.rb +21 -0
  221. data/spec/unit/mutant/{runner → parallel}/driver_spec.rb +0 -0
  222. data/spec/unit/mutant/parallel/master_spec.rb +13 -13
  223. data/spec/unit/mutant/registry_spec.rb +47 -0
  224. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +0 -4
  225. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +0 -8
  226. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +0 -2
  227. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +0 -8
  228. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +0 -34
  229. data/spec/unit/mutant/reporter/cli_spec.rb +0 -22
  230. data/spec/unit/mutant/repository/diff_spec.rb +6 -6
  231. data/spec/unit/mutant/require_highjack_spec.rb +38 -14
  232. data/spec/unit/mutant/result/env_spec.rb +1 -4
  233. data/spec/unit/mutant/runner_spec.rb +1 -1
  234. data/spec/unit/mutant/subject/method/instance_spec.rb +1 -1
  235. data/spec/unit/mutant/subject_spec.rb +3 -3
  236. data/spec/unit/mutant/util/one_spec.rb +20 -0
  237. data/spec/unit/mutant/zombifier_spec.rb +18 -18
  238. data/test_app/{Gemfile.rspec3.3 → Gemfile.rspec3.5} +2 -2
  239. metadata +94 -24
  240. data/TODO +0 -21
  241. data/lib/mutant/mutator/node/blockarg.rb +0 -13
  242. data/lib/mutant/mutator/node/restarg.rb +0 -13
  243. data/lib/mutant/mutator/registry.rb +0 -49
  244. data/meta/boolean.rb +0 -13
  245. data/meta/regex.rb +0 -19
  246. data/spec/unit/mutant/isolation_spec.rb +0 -104
  247. data/spec/unit/mutant/mutator/registry_spec.rb +0 -57
@@ -0,0 +1,55 @@
1
+ module Mutant
2
+ module AST
3
+ module Regexp
4
+ class Transformer
5
+ # Regexp AST transformer for nodes that encode a text value
6
+ class Text < self
7
+ # Mapper from `Regexp::Expression` to `Parser::AST::Node`
8
+ class ExpressionToAST < Transformer::ExpressionToAST
9
+ # Transform expression into node preserving text value
10
+ #
11
+ # @return [Parser::AST::Node]
12
+ def call
13
+ quantify(ast(expression.text))
14
+ end
15
+ end # ExpressionToAST
16
+
17
+ # Mapper from `Parser::AST::Node` to `Regexp::Expression`
18
+ class ASTToExpression < Transformer::ASTToExpression
19
+ include LookupTable
20
+
21
+ TABLE = Table.create(
22
+ [:regexp_literal_literal, %i[literal literal], ::Regexp::Expression::Literal],
23
+ [:regexp_comment_group, %i[group comment], ::Regexp::Expression::Group::Comment],
24
+ [:regexp_named_group, %i[group named], ::Regexp::Expression::Group::Named],
25
+ [:regexp_number_backref, %i[backref number], ::Regexp::Expression::Backreference::Number],
26
+ [:regexp_name_call_backref, %i[backref name_call], ::Regexp::Expression::Backreference::NameCall],
27
+ [:regexp_whitespace_free_space, %i[free_space whitespace], ::Regexp::Expression::WhiteSpace],
28
+ [:regexp_comment_free_space, %i[free_space comment], ::Regexp::Expression::WhiteSpace],
29
+ [:regexp_hex_escape, %i[escape hex], ::Regexp::Expression::EscapeSequence::Literal],
30
+ [:regexp_literal_escape, %i[escape literal], ::Regexp::Expression::EscapeSequence::Literal],
31
+ [:regexp_backslash_escape, %i[escape backslash], ::Regexp::Expression::EscapeSequence::Literal],
32
+ [:regexp_tab_escape, %i[escape tab], ::Regexp::Expression::EscapeSequence::Literal],
33
+ [:regexp_codepoint_list_escape, %i[escape codepoint_list], ::Regexp::Expression::EscapeSequence::Literal],
34
+ [:regexp_control_escape, %i[escape control], ::Regexp::Expression::EscapeSequence::Control],
35
+ [:regexp_meta_sequence_escape, %i[escape meta_sequence], ::Regexp::Expression::EscapeSequence::Control]
36
+ )
37
+
38
+ private
39
+
40
+ # Transform node to expression with text value
41
+ #
42
+ # @return [Regexp::Expression]
43
+ def transform
44
+ token = expression_token.dup
45
+ token.text = Util.one(node.children)
46
+ expression_class.new(token)
47
+ end
48
+ end # ASTToExpression
49
+
50
+ ASTToExpression::TABLE.types.each(&method(:register))
51
+ end # Text
52
+ end # Transformer
53
+ end # Regexp
54
+ end # AST
55
+ end # Mutant
@@ -1,6 +1,9 @@
1
1
  module Mutant
2
2
  module AST
3
3
  # Groups of node types
4
+ #
5
+ # :reek:TooManyConstants
6
+ # rubocop:disable Metrics/ModuleLength
4
7
  module Types
5
8
  symbolset = ->(strings) { strings.map(&:to_sym).to_set.freeze }
6
9
 
@@ -38,15 +41,99 @@ module Mutant
38
41
  #
39
42
  BLACKLIST = symbolset.(%w[not])
40
43
 
44
+ # Nodes generated by regular expression body parsing
45
+ REGEXP = symbolset.(%w[
46
+ regexp_alpha_property
47
+ regexp_alternation_escape
48
+ regexp_alternation_meta
49
+ regexp_atomic_group
50
+ regexp_backslash_escape
51
+ regexp_bell_escape
52
+ regexp_bol_anchor
53
+ regexp_bos_anchor
54
+ regexp_capture_group
55
+ regexp_carriage_escape
56
+ regexp_character_set
57
+ regexp_character_set
58
+ regexp_codepoint_list_escape
59
+ regexp_comment_free_space
60
+ regexp_comment_group
61
+ regexp_control_escape
62
+ regexp_digit_type
63
+ regexp_dot_escape
64
+ regexp_dot_meta
65
+ regexp_eol_anchor
66
+ regexp_eol_escape
67
+ regexp_eos_anchor
68
+ regexp_eos_ob_eol_anchor
69
+ regexp_escape_escape
70
+ regexp_form_feed_escape
71
+ regexp_greedy_interval
72
+ regexp_greedy_one_or_more
73
+ regexp_greedy_zero_or_more
74
+ regexp_greedy_zero_or_one
75
+ regexp_group_close_escape
76
+ regexp_group_open_escape
77
+ regexp_hex_escape
78
+ regexp_hex_type
79
+ regexp_interval_close_escape
80
+ regexp_interval_open_escape
81
+ regexp_letter_any_property
82
+ regexp_literal_escape
83
+ regexp_literal_literal
84
+ regexp_lookahead_assertion
85
+ regexp_lookbehind_assertion
86
+ regexp_mark_keep
87
+ regexp_match_start_anchor
88
+ regexp_meta_sequence_escape
89
+ regexp_name_call_backref
90
+ regexp_named_group
91
+ regexp_newline_escape
92
+ regexp_nlookahead_assertion
93
+ regexp_nlookbehind_assertion
94
+ regexp_nondigit_type
95
+ regexp_nonspace_type
96
+ regexp_nonword_boundary_anchor
97
+ regexp_nonword_type
98
+ regexp_nonhex_type
99
+ regexp_number_backref
100
+ regexp_one_or_more_escape
101
+ regexp_open_conditional
102
+ regexp_options_group
103
+ regexp_passive_group
104
+ regexp_possessive_interval
105
+ regexp_possessive_one_or_more
106
+ regexp_possessive_zero_or_more
107
+ regexp_possessive_zero_or_one
108
+ regexp_reluctant_interval
109
+ regexp_reluctant_one_or_more
110
+ regexp_reluctant_zero_or_more
111
+ regexp_root_expression
112
+ regexp_script_arabic_property
113
+ regexp_script_han_property
114
+ regexp_script_hangul_property
115
+ regexp_script_hiragana_property
116
+ regexp_script_katakana_property
117
+ regexp_sequence_expression
118
+ regexp_set_close_escape
119
+ regexp_set_open_escape
120
+ regexp_space_type
121
+ regexp_tab_escape
122
+ regexp_vertical_tab_escape
123
+ regexp_whitespace_free_space
124
+ regexp_word_boundary_anchor
125
+ regexp_word_type
126
+ regexp_zero_or_more_escape
127
+ regexp_zero_or_one_escape
128
+ ])
129
+
41
130
  # Nodes that are NOT generated by parser but used by mutant / unparser.
42
- EXTRA = symbolset.(%w[empty])
131
+ GENERATED = symbolset.(%w[empty])
43
132
 
44
- # Nodes that are currently missing from Parser::Meta::NODE_TYPES
45
- # * https://github.com/whitequark/parser/pull/251
46
- MISSING = symbolset.(%w[csend])
133
+ EXTRA = symbolset.(GENERATED + REGEXP)
47
134
 
48
135
  # All node types mutant handles
49
- ALL = symbolset.((Parser::Meta::NODE_TYPES + EXTRA + MISSING) - BLACKLIST)
136
+ ALL = symbolset.((Parser::Meta::NODE_TYPES + EXTRA) - BLACKLIST)
50
137
  end # Types
51
138
  end # AST
52
139
  end # Mutant
@@ -1,8 +1,5 @@
1
1
  module Mutant
2
-
3
- # Commandline parser
4
- #
5
- # rubocop:disable ClassLength
2
+ # Commandline parser / runner
6
3
  class CLI
7
4
  include Adamantium::Flat, Equalizer.new(:config), Procto.call(:config)
8
5
 
@@ -103,7 +100,7 @@ module Mutant
103
100
  #
104
101
  # @return [undefined]
105
102
  def setup_integration(name)
106
- with(integration: Integration.setup(name))
103
+ with(integration: Integration.setup(config.kernel, name))
107
104
  rescue LoadError
108
105
  raise Error, "Could not load integration #{name.inspect} (you may want to try installing the gem mutant-#{name})"
109
106
  end
@@ -117,12 +114,6 @@ module Mutant
117
114
  opts.separator(nil)
118
115
  opts.separator('Options:')
119
116
 
120
- opts.on(
121
- '--expected-coverage COVERAGE',
122
- 'Fail unless COVERAGE is not reached exactly, parsed via Rational()'
123
- ) do |coverage|
124
- with(expected_coverage: Rational(coverage))
125
- end
126
117
  opts.on('--use INTEGRATION', 'Use INTEGRATION to kill mutations', &method(:setup_integration))
127
118
  end
128
119
 
@@ -162,9 +153,6 @@ module Mutant
162
153
  puts("mutant-#{VERSION}")
163
154
  config.kernel.exit
164
155
  end
165
- opts.on('-d', '--debug', 'Enable debugging output') do
166
- with(debug: true)
167
- end
168
156
  opts.on_tail('-h', '--help', 'Show this message') do
169
157
  puts(opts.to_s)
170
158
  config.kernel.exit
@@ -24,7 +24,7 @@ module Mutant
24
24
  text
25
25
  end
26
26
 
27
- private
27
+ private # rubocop:disable AccessModifierIndentation
28
28
 
29
29
  # Initialize null color
30
30
  #
@@ -5,8 +5,6 @@ module Mutant
5
5
  # to current environment is being represented by the Mutant::Env object.
6
6
  class Config
7
7
  include Adamantium::Flat, Anima.new(
8
- :debug,
9
- :expected_coverage,
10
8
  :expression_parser,
11
9
  :fail_fast,
12
10
  :integration,
@@ -23,7 +21,7 @@ module Mutant
23
21
  :zombie
24
22
  )
25
23
 
26
- %i[fail_fast zombie debug].each do |name|
24
+ %i[fail_fast zombie].each do |name|
27
25
  define_method(:"#{name}?") { public_send(name) }
28
26
  end
29
27
 
@@ -55,6 +55,6 @@ module Mutant
55
55
  Object.const_get(scope_name)
56
56
  end
57
57
 
58
- end # Method
58
+ end # Methods
59
59
  end # Expression
60
60
  end # Mutant
@@ -57,7 +57,7 @@ module Mutant
57
57
  MATCHER = Matcher::Scope
58
58
  private_constant(*constants(false))
59
59
 
60
- REGEXP = /\A#{SCOPE_NAME_PATTERN}\z/.freeze
60
+ REGEXP = /\A#{SCOPE_NAME_PATTERN}\z/.freeze
61
61
 
62
62
  # Matcher matcher on expression
63
63
  #
@@ -74,5 +74,5 @@ module Mutant
74
74
 
75
75
  end # Exact
76
76
  end # Namespace
77
- end # Namespace
77
+ end # Expression
78
78
  end # Mutant
@@ -5,7 +5,7 @@ module Mutant
5
5
 
6
6
  class ParserError < RuntimeError
7
7
  include AbstractType
8
- end
8
+ end # ParserError
9
9
 
10
10
  # Error raised on invalid expressions
11
11
  class InvalidExpressionError < ParserError; end
@@ -4,38 +4,23 @@ module Mutant
4
4
  class Integration
5
5
  include AbstractType, Adamantium::Flat, Concord.new(:config)
6
6
 
7
- REGISTRY = {}
8
-
9
7
  # Setup integration
10
8
  #
11
- # @param [String] name
9
+ # Integrations are supposed to define a constant under
10
+ # Mutant::Integration named after the capitalized +name+
11
+ # parameter.
12
12
  #
13
- # @return [Integration]
14
- def self.setup(name)
15
- require "mutant/integration/#{name}"
16
- lookup(name)
17
- end
18
-
19
- # Lookup integration for name
13
+ # This avoids having to maintain a mutable registry.
20
14
  #
21
- # @param [String] name
15
+ # @param kernel [Kernel]
16
+ # @param name [String]
22
17
  #
23
- # @return [Integration]
24
- # if found
25
- def self.lookup(name)
26
- REGISTRY.fetch(name)
18
+ # @return [Class<Integration>]
19
+ def self.setup(kernel, name)
20
+ kernel.require("mutant/integration/#{name}")
21
+ const_get(name.capitalize)
27
22
  end
28
23
 
29
- # Register integration
30
- #
31
- # @param [String] name
32
- #
33
- # @return [undefined]
34
- def self.register(name)
35
- REGISTRY[name] = self
36
- end
37
- private_class_method :register
38
-
39
24
  # Perform integration setup
40
25
  #
41
26
  # @return [self]
@@ -67,8 +52,6 @@ module Mutant
67
52
  # Null integration that never kills a mutation
68
53
  class Null < self
69
54
 
70
- register('null')
71
-
72
55
  # Available tests for integration
73
56
  #
74
57
  # @return [Enumerable<Test>]
@@ -91,6 +74,5 @@ module Mutant
91
74
  end
92
75
 
93
76
  end # Null
94
-
95
77
  end # Integration
96
78
  end # Mutant
@@ -1,62 +1,11 @@
1
1
  module Mutant
2
- # Module providing isolation
3
- module Isolation
4
- Error = Class.new(RuntimeError)
5
-
6
- module None
7
-
8
- # Call block in isolation
9
- #
10
- # @return [Object]
11
- #
12
- # @raise [Error]
13
- # if block terminates abnormal
14
- def self.call(&block)
15
- block.call
16
- rescue => exception
17
- raise Error, exception
18
- end
19
- end
20
-
21
- module Fork
22
-
23
- # Call block in isolation
24
- #
25
- # This isolation implements the fork strategy.
26
- # Future strategies will probably use a process pool that can
27
- # handle multiple mutation kills, in-isolation at once.
28
- #
29
- # @return [Object]
30
- # returns block execution result
31
- #
32
- # @raise [Error]
33
- # if block terminates abnormal
34
- #
35
- # rubocop:disable MethodLength
36
- def self.call(&block)
37
- IO.pipe(binmode: true) do |reader, writer|
38
- writer.binmode
39
- begin
40
- pid = Process.fork do
41
- File.open(File::NULL, File::WRONLY) do |file|
42
- $stderr.reopen(file)
43
- reader.close
44
- writer.write(Marshal.dump(block.call))
45
- writer.close
46
- end
47
- end
48
-
49
- writer.close
50
- Marshal.load(reader.read)
51
- ensure
52
- Process.waitpid(pid) if pid
53
- end
54
- end
55
- rescue => exception
56
- raise Error, exception
57
- end
58
-
59
- end # Fork
60
-
61
- end # Isolator
2
+ class Isolation
3
+ include AbstractType
4
+
5
+ # Call block in isolation
6
+ #
7
+ # @return [Object]
8
+ # the blocks result
9
+ abstract_method :call
10
+ end # Isolation
62
11
  end # Mutant
@@ -0,0 +1,72 @@
1
+ module Mutant
2
+ class Isolation
3
+ # Isolation via the fork(2) systemcall.
4
+ #
5
+ # We do inject so many globals and common patterns to make this unit
6
+ # specifiable without mocking the globals and more important: Not having
7
+ # mutations that bypass mocks into a real world side effect.
8
+ class Fork < self
9
+ include Anima.new(:process, :stderr, :stdout, :io, :devnull, :marshal)
10
+
11
+ # Prevent mutation from `process.fork` to `fork` to call Kernel#fork
12
+ undef_method :fork
13
+
14
+ # Call block in isolation
15
+ #
16
+ # @return [Object]
17
+ # returns block execution result
18
+ #
19
+ # @raise [Error]
20
+ # if block terminates abnormal
21
+ def call(&block)
22
+ io.pipe(binmode: true) do |pipes|
23
+ parent(*pipes, &block)
24
+ end
25
+ rescue => exception
26
+ raise Error, exception
27
+ end
28
+
29
+ # Handle parent process
30
+ #
31
+ # @param [IO] reader
32
+ # @param [IO] writer
33
+ #
34
+ # @return [undefined]
35
+ def parent(reader, writer, &block)
36
+ pid = process.fork do
37
+ child(reader, writer, &block)
38
+ end
39
+
40
+ writer.close
41
+ marshal.load(reader)
42
+ ensure
43
+ process.waitpid(pid) if pid
44
+ end
45
+
46
+ # Handle child process
47
+ #
48
+ # @param [IO] reader
49
+ # @param [IO] writer
50
+ #
51
+ # @return [undefined]
52
+ def child(reader, writer, &block)
53
+ reader.close
54
+ writer.binmode
55
+ writer.syswrite(marshal.dump(result(&block)))
56
+ writer.close
57
+ end
58
+
59
+ # The block result computed under silencing
60
+ #
61
+ # @return [Object]
62
+ def result
63
+ devnull.call do |null|
64
+ stderr.reopen(null)
65
+ stdout.reopen(null)
66
+ yield
67
+ end
68
+ end
69
+
70
+ end # Fork
71
+ end # Isolation
72
+ end # Mutant