mutant 0.10.25 → 0.10.26

Sign up to get free protection for your applications and to get access to all the features.
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
  #