mutant 0.10.24 → 0.10.29

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +15 -11
  3. data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
  4. data/lib/mutant/ast/regexp.rb +17 -0
  5. data/lib/mutant/ast/regexp/transformer.rb +2 -2
  6. data/lib/mutant/ast/regexp/transformer/quantifier.rb +4 -2
  7. data/lib/mutant/bootstrap.rb +1 -1
  8. data/lib/mutant/cli/command.rb +4 -0
  9. data/lib/mutant/cli/command/environment/subject.rb +0 -4
  10. data/lib/mutant/cli/command/environment/test.rb +36 -0
  11. data/lib/mutant/cli/command/root.rb +1 -1
  12. data/lib/mutant/config.rb +1 -1
  13. data/lib/mutant/context.rb +1 -1
  14. data/lib/mutant/env.rb +2 -2
  15. data/lib/mutant/expression/method.rb +4 -4
  16. data/lib/mutant/expression/methods.rb +5 -4
  17. data/lib/mutant/integration.rb +8 -2
  18. data/lib/mutant/isolation/exception.rb +22 -0
  19. data/lib/mutant/isolation/fork.rb +9 -12
  20. data/lib/mutant/loader.rb +1 -1
  21. data/lib/mutant/matcher.rb +1 -1
  22. data/lib/mutant/matcher/config.rb +6 -3
  23. data/lib/mutant/matcher/method.rb +12 -10
  24. data/lib/mutant/matcher/method/instance.rb +6 -2
  25. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  26. data/lib/mutant/matcher/methods.rb +2 -4
  27. data/lib/mutant/meta/example.rb +1 -1
  28. data/lib/mutant/meta/example/dsl.rb +0 -1
  29. data/lib/mutant/meta/example/verification.rb +1 -1
  30. data/lib/mutant/mutation.rb +1 -1
  31. data/lib/mutant/mutator.rb +8 -1
  32. data/lib/mutant/mutator/node.rb +0 -5
  33. data/lib/mutant/mutator/node/argument.rb +2 -2
  34. data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
  35. data/lib/mutant/mutator/node/index.rb +1 -0
  36. data/lib/mutant/mutator/node/literal/float.rb +1 -3
  37. data/lib/mutant/mutator/node/literal/integer.rb +3 -6
  38. data/lib/mutant/mutator/node/literal/range.rb +1 -1
  39. data/lib/mutant/mutator/node/literal/regex.rb +3 -17
  40. data/lib/mutant/mutator/node/module.rb +19 -0
  41. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  42. data/lib/mutant/mutator/node/regexp.rb +0 -11
  43. data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
  44. data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
  45. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  46. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
  47. data/lib/mutant/mutator/node/regopt.rb +1 -1
  48. data/lib/mutant/mutator/node/sclass.rb +1 -1
  49. data/lib/mutant/mutator/node/send.rb +73 -6
  50. data/lib/mutant/parallel.rb +2 -2
  51. data/lib/mutant/parallel/driver.rb +1 -1
  52. data/lib/mutant/parallel/worker.rb +1 -1
  53. data/lib/mutant/parser.rb +1 -1
  54. data/lib/mutant/pipe.rb +1 -1
  55. data/lib/mutant/procto.rb +23 -0
  56. data/lib/mutant/reporter/cli/printer.rb +10 -4
  57. data/lib/mutant/reporter/cli/printer/env.rb +3 -3
  58. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  59. data/lib/mutant/reporter/cli/printer/isolation_result.rb +3 -1
  60. data/lib/mutant/selector.rb +1 -1
  61. data/lib/mutant/subject.rb +1 -1
  62. data/lib/mutant/subject/method/instance.rb +5 -42
  63. data/lib/mutant/transform.rb +25 -0
  64. data/lib/mutant/variable.rb +322 -0
  65. data/lib/mutant/version.rb +1 -1
  66. data/lib/mutant/world.rb +1 -1
  67. metadata +13 -160
  68. data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +0 -24
@@ -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
  #
data/lib/mutant/pipe.rb CHANGED
@@ -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
  #
@@ -28,6 +28,7 @@ module Mutant
28
28
  ```
29
29
  %s
30
30
  %s
31
+ %s
31
32
  ```
32
33
  MESSAGE
33
34
 
@@ -81,7 +82,8 @@ module Mutant
81
82
 
82
83
  puts(
83
84
  EXCEPTION_ERROR_MESSAGE % [
84
- exception.inspect,
85
+ exception.original_class,
86
+ exception.message,
85
87
  exception.backtrace.join("\n")
86
88
  ]
87
89
  )
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Abstract base class for test selectors
5
5
  class Selector
6
- include AbstractType, Adamantium::Flat
6
+ include AbstractType, Adamantium
7
7
 
8
8
  # Tests for subject
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Subject of a mutation
5
5
  class Subject
6
- include AbstractType, Adamantium::Flat, Enumerable
6
+ include AbstractType, Adamantium, Enumerable
7
7
  include Anima.new(:context, :node)
8
8
 
9
9
  # Mutations for this subject
@@ -21,59 +21,22 @@ module Mutant
21
21
  class Memoized < self
22
22
  include AST::Sexp
23
23
 
24
- FREEZER_OPTION_VALUES = {
25
- Adamantium::Freezer::Deep => :deep,
26
- Adamantium::Freezer::Flat => :flat,
27
- Adamantium::Freezer::Noop => :noop
28
- }.freeze
29
-
30
- private_constant(*constants(false))
31
-
32
24
  # Prepare subject for mutation insertion
33
25
  #
34
26
  # @return [self]
35
27
  def prepare
36
- memory.delete(name)
28
+ scope
29
+ .instance_variable_get(:@memoized_methods)
30
+ .delete(name)
31
+
37
32
  super()
38
33
  end
39
34
 
40
35
  private
41
36
 
42
37
  def wrap_node(mutant)
43
- s(:begin, mutant, s(:send, nil, :memoize, s(:sym, name), *options))
38
+ s(:begin, mutant, s(:send, nil, :memoize, s(:sym, name)))
44
39
  end
45
-
46
- # The optional AST node for adamantium memoization options
47
- #
48
- # @return [Array(Parser::AST::Node), nil]
49
- def options
50
- # rubocop:disable Style/GuardClause
51
- if FREEZER_OPTION_VALUES.key?(freezer)
52
- [
53
- s(:kwargs,
54
- s(:pair,
55
- s(:sym, :freezer),
56
- s(:sym, FREEZER_OPTION_VALUES.fetch(freezer))))
57
- ]
58
- end
59
- # rubocop:enable Style/GuardClause
60
- end
61
-
62
- # The freezer used for memoization
63
- #
64
- # @return [Object]
65
- def freezer
66
- memory.fetch(name).instance_variable_get(:@freezer)
67
- end
68
- memoize :freezer, freezer: :noop
69
-
70
- # The memory used for memoization
71
- #
72
- # @return [ThreadSafe::Cache]
73
- def memory
74
- scope.__send__(:memoized_methods).instance_variable_get(:@memory)
75
- end
76
-
77
40
  end # Memoized
78
41
  end # Instance
79
42
  end # Method