mutant 0.10.20 → 0.10.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +23 -4
  3. data/lib/mutant/ast/meta/send.rb +0 -1
  4. data/lib/mutant/ast/regexp.rb +37 -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 +90 -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 -11
  14. data/lib/mutant/cli/command/environment.rb +9 -3
  15. data/lib/mutant/config.rb +8 -54
  16. data/lib/mutant/config/coverage_criteria.rb +61 -0
  17. data/lib/mutant/env.rb +8 -6
  18. data/lib/mutant/expression.rb +0 -12
  19. data/lib/mutant/expression/namespace.rb +1 -1
  20. data/lib/mutant/isolation/fork.rb +1 -1
  21. data/lib/mutant/loader.rb +1 -1
  22. data/lib/mutant/matcher.rb +2 -2
  23. data/lib/mutant/matcher/config.rb +27 -6
  24. data/lib/mutant/matcher/method.rb +2 -3
  25. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  26. data/lib/mutant/meta/example/dsl.rb +6 -1
  27. data/lib/mutant/mutator/node/arguments.rb +0 -2
  28. data/lib/mutant/mutator/node/block.rb +5 -1
  29. data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
  30. data/lib/mutant/mutator/node/kwargs.rb +44 -0
  31. data/lib/mutant/mutator/node/literal/regex.rb +26 -0
  32. data/lib/mutant/mutator/node/literal/symbol.rb +0 -2
  33. data/lib/mutant/mutator/node/regexp.rb +20 -0
  34. data/lib/mutant/mutator/node/regexp/alternation_meta.rb +20 -0
  35. data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
  36. data/lib/mutant/mutator/node/regexp/capture_group.rb +25 -0
  37. data/lib/mutant/mutator/node/regexp/character_type.rb +31 -0
  38. data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +20 -0
  39. data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +20 -0
  40. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
  41. data/lib/mutant/mutator/node/sclass.rb +1 -1
  42. data/lib/mutant/mutator/node/send.rb +36 -19
  43. data/lib/mutant/parallel.rb +43 -28
  44. data/lib/mutant/parallel/driver.rb +9 -3
  45. data/lib/mutant/parallel/worker.rb +60 -2
  46. data/lib/mutant/pipe.rb +94 -0
  47. data/lib/mutant/reporter/cli/printer/env.rb +1 -1
  48. data/lib/mutant/reporter/cli/printer/isolation_result.rb +1 -6
  49. data/lib/mutant/result.rb +8 -0
  50. data/lib/mutant/runner.rb +7 -10
  51. data/lib/mutant/runner/sink.rb +12 -2
  52. data/lib/mutant/subject.rb +1 -3
  53. data/lib/mutant/subject/method/instance.rb +2 -4
  54. data/lib/mutant/timer.rb +2 -4
  55. data/lib/mutant/transform.rb +25 -2
  56. data/lib/mutant/version.rb +1 -1
  57. data/lib/mutant/world.rb +1 -2
  58. metadata +48 -9
  59. data/lib/mutant/warnings.rb +0 -106
@@ -19,6 +19,7 @@ module Mutant
19
19
  end
20
20
 
21
21
  def dispatch
22
+ mutate_body
22
23
  emit_singletons unless parent_node
23
24
  children.each_with_index do |child, index|
24
25
  mutate_child(index) unless n_str?(child)
@@ -27,6 +28,31 @@ module Mutant
27
28
  emit_type(s(:str, NULL_REGEXP_SOURCE), options)
28
29
  end
29
30
 
31
+ # NOTE: will only mutate parts of regexp body if the
32
+ # body is composed of only strings. Regular expressions
33
+ # with interpolation are skipped
34
+ def mutate_body
35
+ return unless body.all?(&method(:n_str?))
36
+
37
+ Mutator.mutate(body_ast).each do |mutation|
38
+ source = AST::Regexp.to_expression(mutation).to_s
39
+ emit_type(s(:str, source), options)
40
+ end
41
+ end
42
+
43
+ def body_ast
44
+ AST::Regexp.to_ast(body_expression)
45
+ end
46
+
47
+ def body_expression
48
+ AST::Regexp.parse(body.map(&:children).join)
49
+ end
50
+ memoize :body_expression
51
+
52
+ def body
53
+ children.slice(0...-1)
54
+ end
55
+
30
56
  end # Regex
31
57
  end # Literal
32
58
  end # Node
@@ -11,8 +11,6 @@ module Mutant
11
11
 
12
12
  children :value
13
13
 
14
- PREFIX = '__mutant__'
15
-
16
14
  private
17
15
 
18
16
  def dispatch
@@ -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 = IceNine.deep_freeze(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,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 = IceNine.deep_freeze(
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
+ )
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
@@ -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
@@ -14,30 +14,35 @@ module Mutant
14
14
  children :receiver, :selector
15
15
 
16
16
  SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
17
- reverse_map: %i[map each],
18
- kind_of?: %i[instance_of?],
17
+ :< => %i[== eql? equal?],
18
+ :<= => %i[< == eql? equal?],
19
+ :== => %i[eql? equal?],
20
+ :=== => %i[is_a?],
21
+ :=~ => %i[match?],
22
+ :> => %i[== eql? equal?],
23
+ :>= => %i[> == eql? equal?],
24
+ __send__: %i[public_send],
25
+ all?: %i[any?],
26
+ any?: %i[all?],
27
+ at: %i[fetch key?],
28
+ eql?: %i[equal?],
29
+ fetch: %i[key?],
30
+ flat_map: %i[map],
31
+ gsub: %i[sub],
19
32
  is_a?: %i[instance_of?],
33
+ kind_of?: %i[instance_of?],
34
+ map: %i[each],
35
+ method: %i[public_method],
36
+ match: %i[match?],
20
37
  reverse_each: %i[each],
38
+ reverse_map: %i[map each],
21
39
  reverse_merge: %i[merge],
22
- map: %i[each],
23
- flat_map: %i[map],
24
40
  send: %i[public_send __send__],
25
- __send__: %i[public_send],
26
- method: %i[public_method],
27
- gsub: %i[sub],
28
- eql?: %i[equal?],
29
- to_s: %i[to_str],
30
- to_i: %i[to_int],
31
41
  to_a: %i[to_ary],
32
42
  to_h: %i[to_hash],
33
- at: %i[fetch key?],
34
- fetch: %i[key?],
35
- values_at: %i[fetch_values],
36
- :== => %i[eql? equal?],
37
- :>= => %i[> == eql? equal?],
38
- :<= => %i[< == eql? equal?],
39
- :> => %i[== eql? equal?],
40
- :< => %i[== eql? equal?]
43
+ to_i: %i[to_int],
44
+ to_s: %i[to_str],
45
+ values_at: %i[fetch_values]
41
46
  )
42
47
 
43
48
  RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
@@ -79,6 +84,7 @@ module Mutant
79
84
  end
80
85
 
81
86
  def emit_selector_specific_mutations
87
+ emit_predicate_mutations
82
88
  emit_array_mutation
83
89
  emit_static_send
84
90
  emit_const_get_mutation
@@ -88,6 +94,13 @@ module Mutant
88
94
  emit_lambda_mutation
89
95
  end
90
96
 
97
+ def emit_predicate_mutations
98
+ return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
99
+
100
+ emit(s(:true))
101
+ emit(s(:false))
102
+ end
103
+
91
104
  def emit_array_mutation
92
105
  return unless selector.equal?(:Array) && possible_kernel_method?
93
106
 
@@ -175,7 +188,11 @@ module Mutant
175
188
  end
176
189
 
177
190
  def emit_argument_propagation
178
- emit_propagation(Mutant::Util.one(arguments)) if arguments.one?
191
+ return unless arguments.one?
192
+
193
+ argument = Mutant::Util.one(arguments)
194
+
195
+ emit_propagation(argument) unless n_kwargs?(argument)
179
196
  end
180
197
 
181
198
  def mutate_receiver
@@ -6,45 +6,61 @@ module Mutant
6
6
 
7
7
  # Run async computation returning driver
8
8
  #
9
+ # @param [World] world
9
10
  # @param [Config] config
10
11
  #
11
12
  # @return [Driver]
12
- def self.async(config)
13
- shared = {
14
- var_active_jobs: shared(Variable::IVar, config, value: Set.new),
15
- var_final: shared(Variable::IVar, config),
16
- var_sink: shared(Variable::IVar, config, value: config.sink)
17
- }
13
+ def self.async(world, config)
14
+ shared = shared_state(world, config)
15
+ workers = workers(world, config, shared)
18
16
 
19
17
  Driver.new(
20
- threads: threads(config, worker(config, **shared)),
18
+ workers: workers,
19
+ threads: threads(world, config, workers),
21
20
  **shared
22
21
  )
23
22
  end
24
23
 
25
- # The worker
26
- #
27
- # @param [Config] config
28
- #
29
- # @return [Worker]
30
- def self.worker(config, **shared)
31
- Worker.new(
32
- processor: config.processor,
33
- var_running: shared(Variable::MVar, config, value: config.jobs),
34
- var_source: shared(Variable::IVar, config, value: config.source),
35
- **shared
36
- )
24
+ def self.workers(world, config, shared)
25
+ Array.new(config.jobs) do |index|
26
+ Worker.start(
27
+ block: config.block,
28
+ index: index,
29
+ process_name: "#{config.process_name}-#{index}",
30
+ world: world,
31
+ **shared
32
+ )
33
+ end
37
34
  end
35
+ private_class_method :workers
36
+
37
+ def self.shared_state(world, config)
38
+ {
39
+ var_active_jobs: shared(Variable::IVar, world, value: Set.new),
40
+ var_final: shared(Variable::IVar, world),
41
+ var_running: shared(Variable::MVar, world, value: config.jobs),
42
+ var_sink: shared(Variable::IVar, world, value: config.sink),
43
+ var_source: shared(Variable::IVar, world, value: config.source)
44
+ }
45
+ end
46
+ private_class_method :shared_state
47
+
48
+ def self.threads(world, config, workers)
49
+ thread = world.thread
38
50
 
39
- def self.threads(config, worker)
40
- Array.new(config.jobs) { config.thread.new(&worker.method(:call)) }
51
+ workers.map do |worker|
52
+ thread.new do
53
+ thread.current.name = "#{config.thread_name}-#{worker.index}"
54
+ worker.call
55
+ end
56
+ end
41
57
  end
42
58
  private_class_method :threads
43
59
 
44
- def self.shared(klass, config, **attributes)
60
+ def self.shared(klass, world, **attributes)
45
61
  klass.new(
46
- condition_variable: config.condition_variable,
47
- mutex: config.mutex,
62
+ condition_variable: world.condition_variable,
63
+ mutex: world.mutex,
48
64
  **attributes
49
65
  )
50
66
  end
@@ -75,13 +91,12 @@ module Mutant
75
91
  # Parallel run configuration
76
92
  class Config
77
93
  include Adamantium::Flat, Anima.new(
78
- :condition_variable,
94
+ :block,
79
95
  :jobs,
80
- :mutex,
81
- :processor,
96
+ :process_name,
82
97
  :sink,
83
98
  :source,
84
- :thread
99
+ :thread_name
85
100
  )
86
101
  end # Config
87
102