mutant 0.10.21 → 0.10.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +32 -13
  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/expression/namespace.rb +1 -1
  28. data/lib/mutant/integration.rb +8 -2
  29. data/lib/mutant/isolation/fork.rb +4 -11
  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 +10 -9
  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 -1
  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/argument.rb +2 -2
  43. data/lib/mutant/mutator/node/block.rb +5 -1
  44. data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
  45. data/lib/mutant/mutator/node/kwargs.rb +44 -0
  46. data/lib/mutant/mutator/node/literal/regex.rb +12 -0
  47. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  48. data/lib/mutant/mutator/node/regexp.rb +20 -0
  49. data/lib/mutant/mutator/node/regexp/alternation_meta.rb +20 -0
  50. data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
  51. data/lib/mutant/mutator/node/regexp/capture_group.rb +25 -0
  52. data/lib/mutant/mutator/node/regexp/character_type.rb +31 -0
  53. data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +20 -0
  54. data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +20 -0
  55. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  56. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
  57. data/lib/mutant/mutator/node/regopt.rb +1 -1
  58. data/lib/mutant/mutator/node/sclass.rb +1 -1
  59. data/lib/mutant/mutator/node/send.rb +55 -6
  60. data/lib/mutant/parallel.rb +2 -2
  61. data/lib/mutant/parallel/driver.rb +1 -1
  62. data/lib/mutant/parallel/worker.rb +1 -1
  63. data/lib/mutant/parser.rb +1 -1
  64. data/lib/mutant/pipe.rb +1 -1
  65. data/lib/mutant/procto.rb +23 -0
  66. data/lib/mutant/reporter/cli/printer.rb +10 -4
  67. data/lib/mutant/reporter/cli/printer/env.rb +3 -3
  68. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  69. data/lib/mutant/selector.rb +1 -1
  70. data/lib/mutant/subject.rb +2 -4
  71. data/lib/mutant/subject/method/instance.rb +6 -45
  72. data/lib/mutant/timer.rb +2 -2
  73. data/lib/mutant/transform.rb +25 -0
  74. data/lib/mutant/variable.rb +322 -0
  75. data/lib/mutant/version.rb +1 -1
  76. data/lib/mutant/world.rb +2 -3
  77. metadata +39 -151
  78. data/lib/mutant/warnings.rb +0 -106
@@ -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,10 +13,12 @@ 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],
@@ -30,6 +32,7 @@ module Mutant
30
32
  is_a?: %i[instance_of?],
31
33
  kind_of?: %i[instance_of?],
32
34
  map: %i[each],
35
+ match: %i[match?],
33
36
  method: %i[public_method],
34
37
  reverse_each: %i[each],
35
38
  reverse_map: %i[map each],
@@ -40,13 +43,17 @@ module Mutant
40
43
  to_i: %i[to_int],
41
44
  to_s: %i[to_str],
42
45
  values_at: %i[fetch_values]
43
- )
46
+ }.freeze.tap { |hash| hash.values(&:freeze) }
44
47
 
45
- RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
48
+ RECEIVER_SELECTOR_REPLACEMENTS = {
46
49
  Date: {
47
50
  parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
48
- }
49
- )
51
+ }.freeze
52
+ }.freeze
53
+
54
+ REGEXP_MATCH_METHODS = %i[=~ match match?].freeze
55
+ REGEXP_START_WITH_NODES = %i[regexp_bos_anchor regexp_literal_literal].freeze
56
+ REGEXP_END_WITH_NODES = %i[regexp_literal_literal regexp_eos_anchor].freeze
50
57
 
51
58
  private
52
59
 
@@ -81,6 +88,8 @@ module Mutant
81
88
  end
82
89
 
83
90
  def emit_selector_specific_mutations
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,42 @@ module Mutant
90
99
  emit_lambda_mutation
91
100
  end
92
101
 
102
+ def emit_start_end_with_mutations # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
103
+ return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
104
+
105
+ argument = Mutant::Util.one(arguments)
106
+
107
+ return unless argument.type.equal?(:regexp) && (
108
+ regexp_ast = AST::Regexp.expand_regexp_ast(argument)
109
+ )
110
+
111
+ regexp_children = regexp_ast.children
112
+
113
+ case regexp_children.map(&:type)
114
+ when REGEXP_START_WITH_NODES
115
+ emit_start_with(regexp_children)
116
+ when REGEXP_END_WITH_NODES
117
+ emit_end_with(regexp_children)
118
+ end
119
+ end
120
+
121
+ def emit_start_with(regexp_nodes)
122
+ literal = Mutant::Util.one(regexp_nodes.last.children)
123
+ emit_type(receiver, :start_with?, s(:str, literal))
124
+ end
125
+
126
+ def emit_end_with(regexp_nodes)
127
+ literal = Mutant::Util.one(regexp_nodes.first.children)
128
+ emit_type(receiver, :end_with?, s(:str, literal))
129
+ end
130
+
131
+ def emit_predicate_mutations
132
+ return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
133
+
134
+ emit(s(:true))
135
+ emit(s(:false))
136
+ end
137
+
93
138
  def emit_array_mutation
94
139
  return unless selector.equal?(:Array) && possible_kernel_method?
95
140
 
@@ -177,7 +222,11 @@ module Mutant
177
222
  end
178
223
 
179
224
  def emit_argument_propagation
180
- emit_propagation(Mutant::Util.one(arguments)) if arguments.one?
225
+ return unless arguments.one?
226
+
227
+ argument = Mutant::Util.one(arguments)
228
+
229
+ emit_propagation(argument) unless n_kwargs?(argument)
181
230
  end
182
231
 
183
232
  def mutate_receiver
@@ -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,
@@ -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
  #
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Pipe abstraction
5
5
  class Pipe
6
- include Adamantium::Flat, Anima.new(:reader, :writer)
6
+ include Adamantium, Anima.new(:reader, :writer)
7
7
 
8
8
  # Run block with pipe in binmode
9
9
  #
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module Procto
5
+ # Define the .call method on +host+
6
+ #
7
+ # @param [Object] host
8
+ # the hosting object
9
+ #
10
+ # @return [undefined]
11
+ #
12
+ # @api private
13
+ def self.included(host)
14
+ host.extend(ClassMethods)
15
+ end
16
+
17
+ module ClassMethods
18
+ def call(*arguments)
19
+ new(*arguments).call
20
+ end
21
+ end
22
+ end # Procto
23
+ end # Unparser
@@ -5,13 +5,19 @@ module Mutant
5
5
  class CLI
6
6
  # CLI runner status printer base class
7
7
  class Printer
8
- include AbstractType,
9
- Adamantium::Flat,
10
- Concord.new(:output, :object),
11
- Procto.call(:run)
8
+ include(
9
+ AbstractType,
10
+ Adamantium,
11
+ Concord.new(:output, :object),
12
+ Procto
13
+ )
12
14
 
13
15
  private_class_method :new
14
16
 
17
+ def call
18
+ run
19
+ end
20
+
15
21
  # Create delegators to object
16
22
  #
17
23
  # @return [undefined]
@@ -15,13 +15,13 @@ module Mutant
15
15
  :test_subject_ratio
16
16
  )
17
17
 
18
- FORMATS = IceNine.deep_freeze([
18
+ FORMATS = [
19
19
  [:info, 'Subjects: %s', :amount_subjects ],
20
20
  [:info, 'Total-Tests: %s', :amount_total_tests ],
21
21
  [:info, 'Selected-Tests: %s', :amount_selected_tests],
22
22
  [:info, 'Tests/Subject: %0.2f avg', :test_subject_ratio ],
23
23
  [:info, 'Mutations: %s', :amount_mutations ]
24
- ])
24
+ ].each(&:freeze)
25
25
 
26
26
  # Run printer
27
27
  #
@@ -33,7 +33,7 @@ module Mutant
33
33
  __send__(report, format, __send__(value))
34
34
  end
35
35
  end
36
- end # EnvProgress
36
+ end # Env
37
37
  end # Printer
38
38
  end # CLI
39
39
  end # Reporter
@@ -18,7 +18,7 @@ module Mutant
18
18
  :runtime
19
19
  )
20
20
 
21
- FORMATS = IceNine.deep_freeze([
21
+ FORMATS = [
22
22
  [:info, 'Results: %s', :amount_mutation_results],
23
23
  [:info, 'Kills: %s', :amount_mutations_killed],
24
24
  [:info, 'Alive: %s', :amount_mutations_alive ],
@@ -28,7 +28,7 @@ module Mutant
28
28
  [:info, 'Overhead: %0.2f%%', :overhead_percent ],
29
29
  [:info, 'Mutations/s: %0.2f', :mutations_per_second ],
30
30
  [:status, 'Coverage: %0.2f%%', :coverage_percent ]
31
- ])
31
+ ].each(&:freeze)
32
32
 
33
33
  # Run printer
34
34
  #