mutant 0.8.10 → 0.8.11

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