mutant 0.10.24 → 0.10.29

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Abstract matcher to find subjects to mutate
5
5
  class Matcher
6
- include Adamantium::Flat, AbstractType
6
+ include Adamantium, AbstractType
7
7
 
8
8
  # Call matcher
9
9
  #
@@ -16,17 +16,20 @@ module Mutant
16
16
  ATTRIBUTE_FORMAT = '%s: [%s]'
17
17
  ENUM_DELIMITER = ','
18
18
  EMPTY_ATTRIBUTES = 'empty'
19
- PRESENTATIONS = IceNine.deep_freeze(
19
+ PRESENTATIONS = {
20
20
  ignore: :syntax,
21
21
  start_expressions: :syntax,
22
22
  subject_filters: :inspect,
23
23
  subjects: :syntax
24
- )
24
+ }.freeze
25
+
25
26
  private_constant(*constants(false))
26
27
 
27
28
  DEFAULT = new(Hash[anima.attribute_names.map { |name| [name, []] }])
28
29
 
29
- expression = ->(input) { Mutant::Config::DEFAULT.expression_parser.call(input) }
30
+ expression = Transform::Block.capture(:expression) do |input|
31
+ Mutant::Config::DEFAULT.expression_parser.call(input)
32
+ end
30
33
 
31
34
  expression_array = Transform::Array.new(expression)
32
35
 
@@ -5,12 +5,9 @@ module Mutant
5
5
  # Abstract base class for method matchers
6
6
  class Method < self
7
7
  include AbstractType,
8
- Adamantium::Flat,
8
+ Adamantium,
9
9
  Concord::Public.new(:scope, :target_method, :evaluator)
10
10
 
11
- # Source locations we cannot acces
12
- BLACKLIST = %w[(eval) <internal:prelude>].to_set.freeze
13
-
14
11
  SOURCE_LOCATION_WARNING_FORMAT =
15
12
  '%s does not have a valid source location, unable to emit subject'
16
13
 
@@ -32,11 +29,13 @@ module Mutant
32
29
  # logic would be implemented directly on the Matcher::Method
33
30
  # instance
34
31
  class Evaluator
35
- include AbstractType,
36
- Adamantium,
37
- Concord.new(:scope, :target_method, :env),
38
- Procto.call,
39
- AST::NodePredicates
32
+ include(
33
+ AbstractType,
34
+ Adamantium,
35
+ Concord.new(:scope, :target_method, :env),
36
+ Procto,
37
+ AST::NodePredicates
38
+ )
40
39
 
41
40
  # Matched subjects
42
41
  #
@@ -51,7 +50,10 @@ module Mutant
51
50
 
52
51
  def skip?
53
52
  location = source_location
54
- if location.nil? || BLACKLIST.include?(location.first)
53
+
54
+ file = location&.first
55
+
56
+ if location.nil? || !file.end_with?('.rb')
55
57
  env.warn(SOURCE_LOCATION_WARNING_FORMAT % target_method)
56
58
  elsif matched_node_path.any?(&method(:n_block?))
57
59
  env.warn(CLOSURE_WARNING_FORMAT % target_method)
@@ -13,9 +13,8 @@ module Mutant
13
13
  #
14
14
  # @return [Matcher::Method::Instance]
15
15
  def self.new(scope, target_method)
16
- name = target_method.name
17
16
  evaluator =
18
- if scope.respond_to?(:memoized?) && scope.memoized?(name)
17
+ if memoized_method?(scope, target_method.name)
19
18
  Evaluator::Memoized
20
19
  else
21
20
  Evaluator
@@ -24,6 +23,11 @@ module Mutant
24
23
  super(scope, target_method, evaluator)
25
24
  end
26
25
 
26
+ def self.memoized_method?(scope, method_name)
27
+ scope < Adamantium && scope.memoized?(method_name)
28
+ end
29
+ private_class_method :memoized_method?
30
+
27
31
  # Instance method specific evaluator
28
32
  class Evaluator < Evaluator
29
33
  SUBJECT_CLASS = Subject::Method::Instance
@@ -80,7 +80,7 @@ module Mutant
80
80
  end # Evaluator
81
81
 
82
82
  private_constant(*constants(false))
83
- end # Singleton
83
+ end # Metaclass
84
84
  end # Method
85
85
  end # Matcher
86
86
  end # Mutant
@@ -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,
@@ -79,7 +79,6 @@ module Mutant
79
79
 
80
80
  def singleton_mutations
81
81
  mutation('nil')
82
- mutation('self')
83
82
  end
84
83
 
85
84
  def regexp_mutations
@@ -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)
@@ -67,11 +67,6 @@ module Mutant
67
67
 
68
68
  def emit_singletons
69
69
  emit_nil
70
- emit_self
71
- end
72
-
73
- def emit_self
74
- emit(N_SELF)
75
70
  end
76
71
 
77
72
  def emit_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
 
@@ -18,7 +18,7 @@ module Mutant
18
18
  end
19
19
  end
20
20
 
21
- end # Dstr
21
+ end # DynamicLiteral
22
22
  end # Node
23
23
  end # Mutator
24
24
  end # Mutant
@@ -17,6 +17,7 @@ module Mutant
17
17
  def dispatch
18
18
  emit_singletons
19
19
  emit_receiver_mutations { |node| !n_nil?(node) }
20
+ emit_type(N_SELF, *children.drop(1))
20
21
  emit(receiver)
21
22
  emit_send_forms
22
23
  emit_drop_mutation
@@ -28,9 +28,7 @@ module Mutant
28
28
  end
29
29
 
30
30
  def values
31
- original = children.first
32
-
33
- [0.0, 1.0, -original]
31
+ [0.0, 1.0]
34
32
  end
35
33
 
36
34
  end # Float
@@ -9,6 +9,8 @@ module Mutant
9
9
 
10
10
  handle(:int)
11
11
 
12
+ children :value
13
+
12
14
  private
13
15
 
14
16
  def dispatch
@@ -17,12 +19,7 @@ module Mutant
17
19
  end
18
20
 
19
21
  def values
20
- [0, 1, -value, value + 1, value - 1]
21
- end
22
-
23
- def value
24
- value, = children
25
- value
22
+ [0, 1, value + 1, value - 1]
26
23
  end
27
24
 
28
25
  end # Integer
@@ -21,7 +21,7 @@ module Mutant
21
21
 
22
22
  def dispatch
23
23
  emit_singletons
24
- emit_lower_bound_mutations
24
+ emit_lower_bound_mutations if lower_bound
25
25
 
26
26
  return unless upper_bound
27
27
 
@@ -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
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ class Module < self
7
+ handle :module
8
+
9
+ children :klass, :body
10
+
11
+ private
12
+
13
+ def dispatch
14
+ emit_body_mutations if body
15
+ end
16
+ end # Module
17
+ end # Node
18
+ end # Mutator
19
+ end # Mutant
@@ -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
 
@@ -14,17 +14,6 @@ module Mutant
14
14
  children.each_index(&method(:mutate_child))
15
15
  end
16
16
  end # RootExpression
17
-
18
- # Mutator for beginning of line anchor `^`
19
- class BeginningOfLineAnchor < Node
20
- handle(:regexp_bol_anchor)
21
-
22
- private
23
-
24
- def dispatch
25
- emit(s(:regexp_bos_anchor))
26
- end
27
- end # BeginningOfLineAnchor
28
17
  end # Regexp
29
18
  end # Node
30
19
  end # Mutator
@@ -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
@@ -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