mutant 0.10.23 → 0.10.28

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +34 -14
  3. data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
  4. data/lib/mutant/ast/regexp.rb +54 -0
  5. data/lib/mutant/ast/regexp/transformer.rb +150 -0
  6. data/lib/mutant/ast/regexp/transformer/direct.rb +121 -0
  7. data/lib/mutant/ast/regexp/transformer/named_group.rb +50 -0
  8. data/lib/mutant/ast/regexp/transformer/options_group.rb +68 -0
  9. data/lib/mutant/ast/regexp/transformer/quantifier.rb +92 -0
  10. data/lib/mutant/ast/regexp/transformer/recursive.rb +56 -0
  11. data/lib/mutant/ast/regexp/transformer/root.rb +28 -0
  12. data/lib/mutant/ast/regexp/transformer/text.rb +58 -0
  13. data/lib/mutant/ast/types.rb +115 -2
  14. data/lib/mutant/bootstrap.rb +1 -1
  15. data/lib/mutant/cli/command.rb +4 -0
  16. data/lib/mutant/cli/command/environment.rb +9 -3
  17. data/lib/mutant/cli/command/environment/subject.rb +0 -4
  18. data/lib/mutant/cli/command/environment/test.rb +36 -0
  19. data/lib/mutant/cli/command/root.rb +1 -1
  20. data/lib/mutant/config.rb +9 -55
  21. data/lib/mutant/config/coverage_criteria.rb +61 -0
  22. data/lib/mutant/context.rb +1 -1
  23. data/lib/mutant/env.rb +2 -2
  24. data/lib/mutant/expression.rb +0 -12
  25. data/lib/mutant/expression/method.rb +4 -4
  26. data/lib/mutant/expression/methods.rb +5 -4
  27. data/lib/mutant/integration.rb +8 -2
  28. data/lib/mutant/isolation/exception.rb +22 -0
  29. data/lib/mutant/isolation/fork.rb +9 -12
  30. data/lib/mutant/loader.rb +1 -1
  31. data/lib/mutant/matcher.rb +3 -3
  32. data/lib/mutant/matcher/config.rb +30 -8
  33. data/lib/mutant/matcher/method.rb +14 -13
  34. data/lib/mutant/matcher/method/instance.rb +6 -2
  35. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  36. data/lib/mutant/matcher/methods.rb +2 -4
  37. data/lib/mutant/meta/example.rb +1 -1
  38. data/lib/mutant/meta/example/dsl.rb +6 -2
  39. data/lib/mutant/meta/example/verification.rb +1 -1
  40. data/lib/mutant/mutation.rb +1 -1
  41. data/lib/mutant/mutator.rb +8 -1
  42. data/lib/mutant/mutator/node.rb +0 -5
  43. data/lib/mutant/mutator/node/argument.rb +2 -2
  44. data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
  45. data/lib/mutant/mutator/node/index.rb +1 -0
  46. data/lib/mutant/mutator/node/kwargs.rb +2 -2
  47. data/lib/mutant/mutator/node/literal/float.rb +1 -3
  48. data/lib/mutant/mutator/node/literal/integer.rb +3 -6
  49. data/lib/mutant/mutator/node/literal/regex.rb +12 -0
  50. data/lib/mutant/mutator/node/module.rb +19 -0
  51. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  52. data/lib/mutant/mutator/node/regexp.rb +20 -0
  53. data/lib/mutant/mutator/node/regexp/alternation_meta.rb +20 -0
  54. data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
  55. data/lib/mutant/mutator/node/regexp/capture_group.rb +25 -0
  56. data/lib/mutant/mutator/node/regexp/character_type.rb +31 -0
  57. data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +20 -0
  58. data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +20 -0
  59. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  60. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
  61. data/lib/mutant/mutator/node/regopt.rb +1 -1
  62. data/lib/mutant/mutator/node/sclass.rb +1 -1
  63. data/lib/mutant/mutator/node/send.rb +73 -6
  64. data/lib/mutant/parallel.rb +2 -2
  65. data/lib/mutant/parallel/driver.rb +1 -1
  66. data/lib/mutant/parallel/worker.rb +1 -1
  67. data/lib/mutant/parser.rb +1 -1
  68. data/lib/mutant/pipe.rb +1 -1
  69. data/lib/mutant/procto.rb +23 -0
  70. data/lib/mutant/reporter/cli/printer.rb +10 -4
  71. data/lib/mutant/reporter/cli/printer/env.rb +3 -3
  72. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  73. data/lib/mutant/reporter/cli/printer/isolation_result.rb +3 -1
  74. data/lib/mutant/selector.rb +1 -1
  75. data/lib/mutant/subject.rb +2 -4
  76. data/lib/mutant/subject/method/instance.rb +6 -45
  77. data/lib/mutant/transform.rb +25 -0
  78. data/lib/mutant/variable.rb +322 -0
  79. data/lib/mutant/version.rb +1 -1
  80. data/lib/mutant/world.rb +2 -3
  81. metadata +38 -149
  82. data/lib/mutant/warnings.rb +0 -106
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for root expression regexp wrapper
8
+ class RootExpression < Node
9
+ handle(:regexp_root_expression)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ children.each_index(&method(:mutate_child))
15
+ end
16
+ end # RootExpression
17
+ end # Regexp
18
+ end # Node
19
+ end # Mutator
20
+ end # Mutant
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for pipe in `/foo|bar/` regexp
8
+ class AlternationMeta < Node
9
+ handle(:regexp_alternation_meta)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ children.each_index(&method(:delete_child))
15
+ end
16
+ end # AlternationMeta
17
+ end # Regexp
18
+ end # Node
19
+ end # Mutator
20
+ end # Mutant
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for beginning of line anchor `^`
8
+ class BeginningOfLineAnchor < Node
9
+ handle(:regexp_bol_anchor)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ emit(s(:regexp_bos_anchor))
15
+ end
16
+ end # BeginningOfLineAnchor
17
+ end # Regexp
18
+ end # Node
19
+ end # Mutator
20
+ end # Mutant
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for regexp capture groups, such as `/(foo)/`
8
+ class CaptureGroup < Node
9
+ handle(:regexp_capture_group)
10
+
11
+ children :group
12
+
13
+ private
14
+
15
+ def dispatch
16
+ return unless group
17
+
18
+ emit(s(:regexp_passive_group, group))
19
+ emit_group_mutations
20
+ end
21
+ end # EndOfLineAnchor
22
+ end # Regexp
23
+ end # Node
24
+ end # Mutator
25
+ end # Mutant
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Character type mutator
8
+ class CharacterType < Node
9
+ map = {
10
+ regexp_digit_type: :regexp_nondigit_type,
11
+ regexp_hex_type: :regexp_nonhex_type,
12
+ regexp_space_type: :regexp_nonspace_type,
13
+ regexp_word_boundary_anchor: :regexp_nonword_boundary_anchor,
14
+ regexp_word_type: :regexp_nonword_type,
15
+ regexp_xgrapheme_type: :regexp_linebreak_type
16
+ }
17
+
18
+ MAP = map.merge(map.invert)
19
+
20
+ handle(*MAP.keys)
21
+
22
+ private
23
+
24
+ def dispatch
25
+ emit(s(MAP.fetch(node.type)))
26
+ end
27
+ end # CharacterType
28
+ end # Regexp
29
+ end # Node
30
+ end # Mutator
31
+ end # Mutant
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for end of line anchor `$`
8
+ class EndOfLineAnchor < Node
9
+ handle(:regexp_eol_anchor)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ emit(s(:regexp_eos_anchor))
15
+ end
16
+ end # EndOfLineAnchor
17
+ end # Regexp
18
+ end # Node
19
+ end # Mutator
20
+ end # Mutant
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for end of line or before end of string anchor `\Z`
8
+ class EndOfStringOrBeforeEndOfLineAnchor < Node
9
+ handle(:regexp_eos_ob_eol_anchor)
10
+
11
+ private
12
+
13
+ def dispatch
14
+ emit(s(:regexp_eos_anchor))
15
+ end
16
+ end # EndOfStringOrBeforeEndOfLineAnchor
17
+ end # Regexp
18
+ end # Node
19
+ end # Mutator
20
+ end # Mutant
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for regexp named capture groups, such as `/(?<foo>bar)/`
8
+ class NamedGroup < Node
9
+ handle(:regexp_named_group)
10
+
11
+ children :name, :group
12
+
13
+ private
14
+
15
+ def dispatch
16
+ return unless group
17
+
18
+ emit_group_mutations
19
+
20
+ # Allows unused captures to be kept and named if they are explicitly prefixed with an
21
+ # underscore, like we allow with unused local variables.
22
+ return if name_underscored?
23
+
24
+ emit(s(:regexp_passive_group, group))
25
+ emit_name_underscore_mutation
26
+ end
27
+
28
+ def emit_name_underscore_mutation
29
+ emit_type("_#{name}", group)
30
+ end
31
+
32
+ def name_underscored?
33
+ name.start_with?('_')
34
+ end
35
+ end # EndOfLineAnchor
36
+ end # Regexp
37
+ end # Node
38
+ end # Mutator
39
+ end # Mutant
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ module Regexp
7
+ # Mutator for zero-or-more quantifier, `*`
8
+ class ZeroOrMore < Node
9
+ MAP = {
10
+ regexp_greedy_zero_or_more: :regexp_greedy_one_or_more,
11
+ regexp_reluctant_zero_or_more: :regexp_reluctant_one_or_more,
12
+ regexp_possessive_zero_or_more: :regexp_possessive_one_or_more
13
+ }.freeze
14
+
15
+ handle(*MAP.keys)
16
+
17
+ children :min, :max, :subject
18
+
19
+ private
20
+
21
+ # Replace:
22
+ # * `/a*/` with `/a+/`
23
+ # * `/a*?/` with `/a+?/`
24
+ # * `/a*+/` with `/a++/`
25
+ def dispatch
26
+ emit(s(MAP.fetch(node.type), *children))
27
+ emit_subject_mutations
28
+ emit(subject)
29
+ end
30
+ end # ZeroOrMore
31
+ end # Regexp
32
+ end # Node
33
+ end # Mutator
34
+ end # Mutant
@@ -7,7 +7,7 @@ module Mutant
7
7
  # Regular expression options mutation
8
8
  class Regopt < self
9
9
 
10
- MUTATED_FLAGS = IceNine.deep_freeze(%i[i])
10
+ MUTATED_FLAGS = %i[i].freeze
11
11
 
12
12
  handle(:regopt)
13
13
 
@@ -14,7 +14,7 @@ module Mutant
14
14
  def dispatch
15
15
  emit_body_mutations if body
16
16
  end
17
- end # Class
17
+ end # Sclass
18
18
  end # Node
19
19
  end # Mutator
20
20
  end # Mutant
@@ -13,23 +13,25 @@ module Mutant
13
13
 
14
14
  children :receiver, :selector
15
15
 
16
- SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
16
+ SELECTOR_REPLACEMENTS = {
17
17
  :< => %i[== eql? equal?],
18
18
  :<= => %i[< == eql? equal?],
19
19
  :== => %i[eql? equal?],
20
+ :=== => %i[is_a?],
21
+ :=~ => %i[match?],
20
22
  :> => %i[== eql? equal?],
21
23
  :>= => %i[> == eql? equal?],
22
24
  __send__: %i[public_send],
23
25
  all?: %i[any?],
24
26
  any?: %i[all?],
25
27
  at: %i[fetch key?],
26
- eql?: %i[equal?],
27
28
  fetch: %i[key?],
28
29
  flat_map: %i[map],
29
30
  gsub: %i[sub],
30
31
  is_a?: %i[instance_of?],
31
32
  kind_of?: %i[instance_of?],
32
33
  map: %i[each],
34
+ match: %i[match?],
33
35
  method: %i[public_method],
34
36
  reverse_each: %i[each],
35
37
  reverse_map: %i[map each],
@@ -40,13 +42,17 @@ module Mutant
40
42
  to_i: %i[to_int],
41
43
  to_s: %i[to_str],
42
44
  values_at: %i[fetch_values]
43
- )
45
+ }.freeze.tap { |hash| hash.values(&:freeze) }
44
46
 
45
- RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
47
+ RECEIVER_SELECTOR_REPLACEMENTS = {
46
48
  Date: {
47
49
  parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
48
- }
49
- )
50
+ }.freeze
51
+ }.freeze
52
+
53
+ REGEXP_MATCH_METHODS = %i[=~ match match?].freeze
54
+ REGEXP_START_WITH_NODES = %i[regexp_bos_anchor regexp_literal_literal].freeze
55
+ REGEXP_END_WITH_NODES = %i[regexp_literal_literal regexp_eos_anchor].freeze
50
56
 
51
57
  private
52
58
 
@@ -81,6 +87,9 @@ module Mutant
81
87
  end
82
88
 
83
89
  def emit_selector_specific_mutations
90
+ emit_reduce_to_sum_mutation
91
+ emit_start_end_with_mutations
92
+ emit_predicate_mutations
84
93
  emit_array_mutation
85
94
  emit_static_send
86
95
  emit_const_get_mutation
@@ -90,6 +99,57 @@ module Mutant
90
99
  emit_lambda_mutation
91
100
  end
92
101
 
102
+ def emit_reduce_to_sum_mutation
103
+ return unless selector.equal?(:reduce)
104
+
105
+ reducer = arguments.last
106
+
107
+ return unless reducer.eql?(s(:sym, :+)) || reducer.eql?(s(:block_pass, s(:sym, :+)))
108
+
109
+ if arguments.length > 1
110
+ initial_value = arguments.first
111
+ emit_type(receiver, :sum, initial_value)
112
+ else
113
+ emit_type(receiver, :sum)
114
+ end
115
+ end
116
+
117
+ def emit_start_end_with_mutations # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
118
+ return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
119
+
120
+ argument = Mutant::Util.one(arguments)
121
+
122
+ return unless argument.type.equal?(:regexp) && (
123
+ regexp_ast = AST::Regexp.expand_regexp_ast(argument)
124
+ )
125
+
126
+ regexp_children = regexp_ast.children
127
+
128
+ case regexp_children.map(&:type)
129
+ when REGEXP_START_WITH_NODES
130
+ emit_start_with(regexp_children)
131
+ when REGEXP_END_WITH_NODES
132
+ emit_end_with(regexp_children)
133
+ end
134
+ end
135
+
136
+ def emit_start_with(regexp_nodes)
137
+ literal = Mutant::Util.one(regexp_nodes.last.children)
138
+ emit_type(receiver, :start_with?, s(:str, literal))
139
+ end
140
+
141
+ def emit_end_with(regexp_nodes)
142
+ literal = Mutant::Util.one(regexp_nodes.first.children)
143
+ emit_type(receiver, :end_with?, s(:str, literal))
144
+ end
145
+
146
+ def emit_predicate_mutations
147
+ return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
148
+
149
+ emit(s(:true))
150
+ emit(s(:false))
151
+ end
152
+
93
153
  def emit_array_mutation
94
154
  return unless selector.equal?(:Array) && possible_kernel_method?
95
155
 
@@ -187,11 +247,18 @@ module Mutant
187
247
  def mutate_receiver
188
248
  return unless receiver
189
249
  emit_implicit_self
250
+ emit_explicit_self
190
251
  emit_receiver_mutations do |node|
191
252
  !n_nil?(node)
192
253
  end
193
254
  end
194
255
 
256
+ def emit_explicit_self
257
+ return if UNARY_METHOD_OPERATORS.include?(selector)
258
+
259
+ emit_receiver(N_SELF) unless n_nil?(receiver)
260
+ end
261
+
195
262
  def emit_implicit_self
196
263
  emit_receiver(nil) if n_self?(receiver) && !(
197
264
  KEYWORDS.include?(selector) ||
@@ -90,7 +90,7 @@ module Mutant
90
90
 
91
91
  # Parallel run configuration
92
92
  class Config
93
- include Adamantium::Flat, Anima.new(
93
+ include Adamantium, Anima.new(
94
94
  :block,
95
95
  :jobs,
96
96
  :process_name,
@@ -102,7 +102,7 @@ module Mutant
102
102
 
103
103
  # Parallel execution status
104
104
  class Status
105
- include Adamantium::Flat, Anima.new(
105
+ include Adamantium, Anima.new(
106
106
  :active_jobs,
107
107
  :done,
108
108
  :payload
@@ -4,7 +4,7 @@ module Mutant
4
4
  module Parallel
5
5
  # Driver for parallelized execution
6
6
  class Driver
7
- include Adamantium::Flat, Anima.new(
7
+ include Adamantium, Anima.new(
8
8
  :threads,
9
9
  :var_active_jobs,
10
10
  :var_final,
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  module Parallel
5
5
  class Worker
6
- include Adamantium::Flat, Anima.new(
6
+ include Adamantium, Anima.new(
7
7
  :connection,
8
8
  :index,
9
9
  :pid,
data/lib/mutant/parser.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # An AST Parser
5
5
  class Parser
6
- include Adamantium::Mutable, Equalizer.new
6
+ include Adamantium, Equalizer.new
7
7
 
8
8
  # Initialize object
9
9
  #