mutant 0.10.25 → 0.10.26

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +10 -9
  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/fork.rb +4 -11
  19. data/lib/mutant/matcher.rb +1 -1
  20. data/lib/mutant/matcher/config.rb +3 -2
  21. data/lib/mutant/matcher/method.rb +8 -6
  22. data/lib/mutant/matcher/method/instance.rb +6 -2
  23. data/lib/mutant/matcher/methods.rb +2 -4
  24. data/lib/mutant/meta/example.rb +1 -1
  25. data/lib/mutant/meta/example/verification.rb +1 -1
  26. data/lib/mutant/mutation.rb +1 -1
  27. data/lib/mutant/mutator.rb +8 -1
  28. data/lib/mutant/mutator/node/argument.rb +2 -2
  29. data/lib/mutant/mutator/node/literal/regex.rb +3 -17
  30. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  31. data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
  32. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  33. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +2 -2
  34. data/lib/mutant/mutator/node/regopt.rb +1 -1
  35. data/lib/mutant/mutator/node/send.rb +40 -6
  36. data/lib/mutant/parallel.rb +2 -2
  37. data/lib/mutant/parallel/driver.rb +1 -1
  38. data/lib/mutant/parallel/worker.rb +1 -1
  39. data/lib/mutant/parser.rb +1 -1
  40. data/lib/mutant/pipe.rb +1 -1
  41. data/lib/mutant/procto.rb +23 -0
  42. data/lib/mutant/reporter/cli/printer.rb +10 -4
  43. data/lib/mutant/reporter/cli/printer/env.rb +2 -2
  44. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  45. data/lib/mutant/selector.rb +1 -1
  46. data/lib/mutant/subject.rb +1 -1
  47. data/lib/mutant/subject/method/instance.rb +5 -42
  48. data/lib/mutant/variable.rb +322 -0
  49. data/lib/mutant/version.rb +1 -1
  50. data/lib/mutant/world.rb +1 -1
  51. metadata +8 -158
@@ -6,11 +6,11 @@ module Mutant
6
6
  class Methods < self
7
7
  include AbstractType, Concord.new(:scope)
8
8
 
9
- CANDIDATE_NAMES = IceNine.deep_freeze(%i[
9
+ CANDIDATE_NAMES = %i[
10
10
  public_instance_methods
11
11
  private_instance_methods
12
12
  protected_instance_methods
13
- ])
13
+ ].freeze
14
14
 
15
15
  private_constant(*constants(false))
16
16
 
@@ -62,7 +62,6 @@ module Mutant
62
62
  def candidate_scope
63
63
  scope.singleton_class
64
64
  end
65
- memoize :candidate_scope, freezer: :noop
66
65
 
67
66
  end # Singleton
68
67
 
@@ -79,7 +78,6 @@ module Mutant
79
78
  def candidate_scope
80
79
  scope.singleton_class
81
80
  end
82
- memoize :candidate_scope, freezer: :noop
83
81
  end # Metaclass
84
82
 
85
83
  # Matcher for instance methods
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  module Meta
5
5
  class Example
6
- include Adamantium::Flat
6
+ include Adamantium
7
7
 
8
8
  include Anima.new(
9
9
  :expected,
@@ -5,7 +5,7 @@ module Mutant
5
5
  class Example
6
6
  # Example verification
7
7
  class Verification
8
- include Adamantium::Flat, Concord.new(:example, :mutations)
8
+ include Adamantium, Concord.new(:example, :mutations)
9
9
 
10
10
  # Test if mutation was verified successfully
11
11
  #
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Represent a mutated node with its subject
5
5
  class Mutation
6
- include AbstractType, Adamantium::Flat
6
+ include AbstractType, Adamantium
7
7
  include Concord::Public.new(:subject, :node)
8
8
 
9
9
  CODE_DELIMITER = "\0"
@@ -6,7 +6,12 @@ module Mutant
6
6
 
7
7
  REGISTRY = Registry.new
8
8
 
9
- include Adamantium::Flat, Concord.new(:input, :parent), AbstractType, Procto.call(:output)
9
+ include(
10
+ Adamantium,
11
+ Concord.new(:input, :parent),
12
+ AbstractType,
13
+ Procto
14
+ )
10
15
 
11
16
  # Lookup and invoke dedicated AST mutator
12
17
  #
@@ -30,6 +35,8 @@ module Mutant
30
35
  # @return [Set<Parser::AST::Node>]
31
36
  attr_reader :output
32
37
 
38
+ alias_method :call, :output
39
+
33
40
  private
34
41
 
35
42
  def initialize(_input, _parent = nil)
@@ -30,10 +30,10 @@ module Mutant
30
30
  # Mutator for optional arguments
31
31
  class Optional < self
32
32
 
33
- TYPE_MAP = IceNine.deep_freeze(
33
+ TYPE_MAP = {
34
34
  optarg: :arg,
35
35
  kwoptarg: :kwarg
36
- )
36
+ }.freeze
37
37
 
38
38
  handle(:optarg, :kwoptarg)
39
39
 
@@ -28,11 +28,10 @@ module Mutant
28
28
  emit_type(s(:str, NULL_REGEXP_SOURCE), options)
29
29
  end
30
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
31
  def mutate_body
35
- return unless body.all?(&method(:n_str?))
32
+ # NOTE: will only mutate parts of regexp body if the body is composed of only strings.
33
+ # Regular expressions with interpolation are skipped.
34
+ return unless (body_ast = AST::Regexp.expand_regexp_ast(input))
36
35
 
37
36
  Mutator.mutate(body_ast).each do |mutation|
38
37
  source = AST::Regexp.to_expression(mutation).to_s
@@ -40,19 +39,6 @@ module Mutant
40
39
  end
41
40
  end
42
41
 
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
-
56
42
  end # Regex
57
43
  end # Literal
58
44
  end # Node
@@ -17,9 +17,9 @@ module Mutant
17
17
  lvasgn: EMPTY_STRING
18
18
  }
19
19
 
20
- MAP = IceNine.deep_freeze(
21
- map.transform_values { |prefix| [prefix, /^#{::Regexp.escape(prefix)}/] }
22
- )
20
+ MAP = map
21
+ .transform_values { |prefix| [prefix, /^#{::Regexp.escape(prefix)}/] }
22
+ .freeze
23
23
 
24
24
  handle(*MAP.keys)
25
25
 
@@ -15,7 +15,7 @@ module Mutant
15
15
  regexp_xgrapheme_type: :regexp_linebreak_type
16
16
  }
17
17
 
18
- MAP = IceNine.deep_freeze(map.merge(map.invert))
18
+ MAP = map.merge(map.invert)
19
19
 
20
20
  handle(*MAP.keys)
21
21
 
@@ -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
@@ -6,11 +6,11 @@ module Mutant
6
6
  module Regexp
7
7
  # Mutator for zero-or-more quantifier, `*`
8
8
  class ZeroOrMore < Node
9
- MAP = IceNine.deep_freeze(
9
+ MAP = {
10
10
  regexp_greedy_zero_or_more: :regexp_greedy_one_or_more,
11
11
  regexp_reluctant_zero_or_more: :regexp_reluctant_one_or_more,
12
12
  regexp_possessive_zero_or_more: :regexp_possessive_one_or_more
13
- )
13
+ }.freeze
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
@@ -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
 
@@ -13,7 +13,7 @@ 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?],
@@ -32,8 +32,8 @@ module Mutant
32
32
  is_a?: %i[instance_of?],
33
33
  kind_of?: %i[instance_of?],
34
34
  map: %i[each],
35
- method: %i[public_method],
36
35
  match: %i[match?],
36
+ method: %i[public_method],
37
37
  reverse_each: %i[each],
38
38
  reverse_map: %i[map each],
39
39
  reverse_merge: %i[merge],
@@ -43,13 +43,17 @@ module Mutant
43
43
  to_i: %i[to_int],
44
44
  to_s: %i[to_str],
45
45
  values_at: %i[fetch_values]
46
- )
46
+ }.freeze.tap { |hash| hash.values(&:freeze) }
47
47
 
48
- RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
48
+ RECEIVER_SELECTOR_REPLACEMENTS = {
49
49
  Date: {
50
50
  parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
51
- }
52
- )
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
53
57
 
54
58
  private
55
59
 
@@ -84,6 +88,7 @@ module Mutant
84
88
  end
85
89
 
86
90
  def emit_selector_specific_mutations
91
+ emit_start_end_with_mutations
87
92
  emit_predicate_mutations
88
93
  emit_array_mutation
89
94
  emit_static_send
@@ -94,6 +99,35 @@ module Mutant
94
99
  emit_lambda_mutation
95
100
  end
96
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
+
97
131
  def emit_predicate_mutations
98
132
  return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
99
133
 
@@ -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
  #
@@ -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
  #
@@ -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
  #