mutant 0.11.28 → 0.11.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant/ast.rb +8 -9
  3. data/lib/mutant/bootstrap.rb +39 -13
  4. data/lib/mutant/cli/command/environment/test.rb +38 -1
  5. data/lib/mutant/cli/command/environment.rb +11 -0
  6. data/lib/mutant/context.rb +31 -61
  7. data/lib/mutant/env.rb +8 -1
  8. data/lib/mutant/expression/method.rb +4 -1
  9. data/lib/mutant/expression/methods.rb +4 -1
  10. data/lib/mutant/expression/namespace.rb +4 -4
  11. data/lib/mutant/hooks.rb +1 -0
  12. data/lib/mutant/integration/null.rb +1 -0
  13. data/lib/mutant/integration.rb +5 -1
  14. data/lib/mutant/matcher/descendants.rb +1 -1
  15. data/lib/mutant/matcher/method/instance.rb +5 -4
  16. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  17. data/lib/mutant/matcher/method/singleton.rb +1 -1
  18. data/lib/mutant/matcher/method.rb +30 -4
  19. data/lib/mutant/matcher/methods.rb +8 -7
  20. data/lib/mutant/matcher/namespace.rb +1 -1
  21. data/lib/mutant/meta/example.rb +12 -2
  22. data/lib/mutant/mutation/runner/sink.rb +7 -3
  23. data/lib/mutant/mutation/runner.rb +2 -3
  24. data/lib/mutant/parallel/connection.rb +178 -0
  25. data/lib/mutant/parallel/pipe.rb +39 -0
  26. data/lib/mutant/parallel/worker.rb +42 -14
  27. data/lib/mutant/parallel.rb +18 -7
  28. data/lib/mutant/reporter/cli/format.rb +19 -2
  29. data/lib/mutant/reporter/cli/printer/test.rb +138 -0
  30. data/lib/mutant/reporter/cli.rb +33 -4
  31. data/lib/mutant/reporter.rb +22 -1
  32. data/lib/mutant/result.rb +53 -2
  33. data/lib/mutant/scope.rb +41 -1
  34. data/lib/mutant/subject/method/instance.rb +3 -2
  35. data/lib/mutant/subject/method/metaclass.rb +1 -1
  36. data/lib/mutant/subject/method/singleton.rb +2 -2
  37. data/lib/mutant/subject/method.rb +1 -1
  38. data/lib/mutant/test/runner/sink.rb +51 -0
  39. data/lib/mutant/test/runner.rb +62 -0
  40. data/lib/mutant/timer.rb +9 -0
  41. data/lib/mutant/version.rb +1 -1
  42. data/lib/mutant/world.rb +2 -0
  43. data/lib/mutant.rb +7 -1
  44. metadata +9 -19
  45. data/lib/mutant/pipe.rb +0 -96
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2580053028745a106b883c1468b4e0c4462d1e5e443f12533ca277149f603239
4
- data.tar.gz: db5d4f28324b0846a9872b3a7b841e8dcdb84ae204f6c15def9f9381db064ea3
3
+ metadata.gz: 3e57360ec50ffdef54f132f4764c940caad83a2d3692ac812b95c236cd8ff52d
4
+ data.tar.gz: cc69f0948238d888f569caeee901dc9197b620a3693474323a0959212d73fc30
5
5
  SHA512:
6
- metadata.gz: 125e846599d5bfbd7ec52d3f7bbcf78f53d2584b7dd447d9313954c10ff4e5176c59fec396833b97f81bcb4bb40d9fdcf1cc0ef138c3a53685af503b0f95678b
7
- data.tar.gz: 2fb0163bfcdc01ed03fb34418a9522af0dab49e4fb8fa91cccf8c9e7cbbdcbd00551e2c36087ca8ee587bda317cc391a0bf7eae564be7834b53eff96873abd55
6
+ metadata.gz: cf4a364cc90b650a6c9dff90b38fb22aaa54d62f98f3b85ae732894a112f89a72b7c6e81b25fdd051b4436e8b55c58203fa4645a1c471cd5745fd665c7f0418b
7
+ data.tar.gz: 27a2195e3cd277816e37970fbbf66ed3d4f5237d9be667345229a9739deebaa45366914c71ce41254b7e0a65ea3e40af6012ddf4f8d654164d5eab904345dcf0
data/lib/mutant/ast.rb CHANGED
@@ -8,12 +8,12 @@ module Mutant
8
8
  )
9
9
 
10
10
  class View
11
- include Adamantium, Anima.new(:node, :path)
11
+ include Adamantium, Anima.new(:node, :stack)
12
12
  end
13
13
 
14
14
  def on_line(line)
15
- line_map.fetch(line, EMPTY_HASH).map do |node, path|
16
- View.new(node: node, path: path)
15
+ line_map.fetch(line, EMPTY_HASH).map do |node, stack|
16
+ View.new(node: node, stack: stack)
17
17
  end
18
18
  end
19
19
 
@@ -22,21 +22,20 @@ module Mutant
22
22
  def line_map
23
23
  line_map = {}
24
24
 
25
- walk_path(node) do |node, path|
25
+ walk_path(node, []) do |node, stack|
26
26
  expression = node.location.expression || next
27
- (line_map[expression.line] ||= []) << [node, path]
27
+ (line_map[expression.line] ||= []) << [node, stack]
28
28
  end
29
29
 
30
30
  line_map
31
31
  end
32
32
  memoize :line_map
33
33
 
34
- def walk_path(node, stack = [node.type], &block)
35
- block.call(node, stack.dup)
34
+ def walk_path(node, stack, &block)
35
+ block.call(node, stack)
36
+ stack = [*stack, node]
36
37
  node.children.grep(::Parser::AST::Node) do |child|
37
- stack.push(child.type)
38
38
  walk_path(child, stack, &block)
39
- stack.pop
40
39
  end
41
40
  end
42
41
  end # AST
@@ -20,7 +20,7 @@ module Mutant
20
20
  '%<scope_class>s#name from: %<scope>s raised an error: %<exception>s'
21
21
 
22
22
  CLASS_NAME_TYPE_MISMATCH_FORMAT =
23
- '%<scope_class>s#name from: %<scope>s returned %<name>s'
23
+ '%<scope_class>s#name from: %<raw_scope>s returned %<name>s'
24
24
 
25
25
  private_constant(*constants(false))
26
26
 
@@ -47,6 +47,32 @@ module Mutant
47
47
  selected_subjects.flat_map(&:mutations)
48
48
  end
49
49
 
50
+ setup_integration(
51
+ env: env,
52
+ mutations: mutations,
53
+ selected_subjects: selected_subjects
54
+ )
55
+ end
56
+ end
57
+ # rubocop:enable Metrics/MethodLength
58
+
59
+ # Run test only bootstrap
60
+ #
61
+ # @param [Env] env
62
+ #
63
+ # @return [Either<String, Env>]
64
+ def self.call_test(env)
65
+ env.record(:bootstrap) do
66
+ setup_integration(
67
+ env: load_hooks(env),
68
+ mutations: [],
69
+ selected_subjects: []
70
+ )
71
+ end
72
+ end
73
+
74
+ def self.setup_integration(env:, mutations:, selected_subjects:)
75
+ env.record(__method__) do
50
76
  Integration.setup(env).fmap do |integration|
51
77
  env.with(
52
78
  integration: integration,
@@ -57,7 +83,7 @@ module Mutant
57
83
  end
58
84
  end
59
85
  end
60
- # rubocop:enable Metrics/MethodLength
86
+ private_class_method :setup_integration
61
87
 
62
88
  def self.load_hooks(env)
63
89
  env.record(__method__) do
@@ -113,9 +139,9 @@ module Mutant
113
139
  env.record(__method__) do
114
140
  config = env.config
115
141
 
116
- scopes = env.world.object_space.each_object(Module).with_object([]) do |scope, aggregate|
117
- expression = expression(config.reporter, config.expression_parser, scope) || next
118
- aggregate << Scope.new(raw: scope, expression: expression)
142
+ scopes = env.world.object_space.each_object(Module).with_object([]) do |raw_scope, aggregate|
143
+ expression = expression(config.reporter, config.expression_parser, raw_scope) || next
144
+ aggregate << Scope.new(raw: raw_scope, expression: expression)
119
145
  end
120
146
 
121
147
  scopes.sort_by { |scope| scope.expression.syntax }
@@ -123,31 +149,31 @@ module Mutant
123
149
  end
124
150
  private_class_method :matchable_scopes
125
151
 
126
- def self.scope_name(reporter, scope)
127
- scope.name
152
+ def self.scope_name(reporter, raw_scope)
153
+ raw_scope.name
128
154
  rescue => exception
129
155
  semantics_warning(
130
156
  reporter,
131
157
  CLASS_NAME_RAISED_EXCEPTION,
132
158
  exception: exception.inspect,
133
- scope: scope,
134
- scope_class: scope.class
159
+ scope: raw_scope,
160
+ scope_class: raw_scope.class
135
161
  )
136
162
  nil
137
163
  end
138
164
  private_class_method :scope_name
139
165
 
140
166
  # rubocop:disable Metrics/MethodLength
141
- def self.expression(reporter, expression_parser, scope)
142
- name = scope_name(reporter, scope) or return
167
+ def self.expression(reporter, expression_parser, raw_scope)
168
+ name = scope_name(reporter, raw_scope) or return
143
169
 
144
170
  unless name.instance_of?(String)
145
171
  semantics_warning(
146
172
  reporter,
147
173
  CLASS_NAME_TYPE_MISMATCH_FORMAT,
148
174
  name: name,
149
- scope_class: scope.class,
150
- scope: scope
175
+ scope_class: raw_scope.class,
176
+ raw_scope: raw_scope
151
177
  )
152
178
  return
153
179
  end
@@ -8,6 +8,21 @@ module Mutant
8
8
  NAME = 'test'
9
9
  SHORT_DESCRIPTION = 'test subcommands'
10
10
 
11
+ private
12
+
13
+ def parse_remaining_arguments(arguments)
14
+ arguments.each(&method(:add_integration_argument))
15
+ Either::Right.new(self)
16
+ end
17
+
18
+ def bootstrap
19
+ env = Env.empty(world, @config)
20
+
21
+ env
22
+ .record(:config) { Config.load(cli_config: @config, world: world) }
23
+ .bind { |config| Bootstrap.call_test(env.with(config: config)) }
24
+ end
25
+
11
26
  class List < self
12
27
  NAME = 'list'
13
28
  SHORT_DESCRIPTION = 'List tests detected in the environment'
@@ -28,7 +43,29 @@ module Mutant
28
43
  end
29
44
  end
30
45
 
31
- SUBCOMMANDS = [List].freeze
46
+ class Run < self
47
+ NAME = 'run'
48
+ SHORT_DESCRIPTION = 'Run tests'
49
+ SUBCOMMANDS = EMPTY_ARRAY
50
+
51
+ private
52
+
53
+ def action
54
+ bootstrap
55
+ .bind(&Mutant::Test::Runner.public_method(:call))
56
+ .bind(&method(:from_result))
57
+ end
58
+
59
+ def from_result(result)
60
+ if result.success?
61
+ Either::Right.new(nil)
62
+ else
63
+ Either::Left.new('Test failures, exiting nonzero!')
64
+ end
65
+ end
66
+ end
67
+
68
+ SUBCOMMANDS = [List, Run].freeze
32
69
  end # Test
33
70
  end # Environment
34
71
  end # Command
@@ -3,6 +3,7 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class Environment < self
7
8
  NAME = 'environment'
8
9
  SHORT_DESCRIPTION = 'Environment subcommands'
@@ -13,6 +14,7 @@ module Mutant
13
14
  add_runner_options
14
15
  add_integration_options
15
16
  add_matcher_options
17
+ add_reporter_options
16
18
  ].freeze
17
19
 
18
20
  private
@@ -126,7 +128,16 @@ module Mutant
126
128
  set(mutation: @config.mutation.with(timeout: Float(number)))
127
129
  end
128
130
  end
131
+
132
+ def add_reporter_options(parser)
133
+ parser.separator('Reporting:')
134
+
135
+ parser.on('--print-warnings', 'Print warnings') do
136
+ set(reporter: @config.reporter.with(print_warnings: true))
137
+ end
138
+ end
129
139
  end # Run
140
+ # rubocop:enable Metrics/ClassLength
130
141
  end # Command
131
142
  end # CLI
132
143
  end # Mutant
@@ -3,83 +3,53 @@
3
3
  module Mutant
4
4
  # An abstract context where mutations can be applied to.
5
5
  class Context
6
- include Adamantium, Anima.new(:scope, :source_path)
7
- extend AST::Sexp
6
+ include Adamantium, Anima.new(:constant_scope, :scope, :source_path)
8
7
 
9
- NAMESPACE_DELIMITER = '::'
8
+ class ConstantScope
9
+ include AST::Sexp
10
10
 
11
- # Return root node for mutation
12
- #
13
- # @return [Parser::AST::Node]
14
- def root(node)
15
- nesting.reverse.reduce(node) do |current, scope|
16
- self.class.wrap(scope, current)
11
+ class Class < self
12
+ include Anima.new(:const, :descendant)
13
+
14
+ def call(node)
15
+ s(:class, const, nil, descendant.call(node))
16
+ end
17
17
  end
18
- end
19
18
 
20
- # Identification string
21
- #
22
- # @return [String]
23
- def identification
24
- scope.name
25
- end
19
+ class Module < self
20
+ include Anima.new(:const, :descendant)
26
21
 
27
- # Wrap node into ast node
28
- #
29
- # @param [Class, Module] scope
30
- # @param [Parser::AST::Node] node
31
- #
32
- # @return [Parser::AST::Class]
33
- # if scope is of kind Class
34
- #
35
- # @return [Parser::AST::Module]
36
- # if scope is of kind module
37
- def self.wrap(scope, node)
38
- name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym)
39
- case scope
40
- when Class
41
- s(:class, name, nil, node)
42
- when Module
43
- s(:module, name, node)
22
+ def call(node)
23
+ s(:module, const, descendant.call(node))
24
+ end
44
25
  end
45
- end
46
26
 
47
- # Nesting of scope
48
- #
49
- # @return [Enumerable<Class,Module>]
50
- def nesting
51
- const = Object
52
- name_nesting.map do |name|
53
- const = const.const_get(name)
27
+ class None < self
28
+ include Equalizer.new
29
+
30
+ def call(node)
31
+ node
32
+ end
54
33
  end
55
34
  end
56
- memoize :nesting
57
35
 
58
- # Unqualified name of scope
59
- #
60
- # @return [String]
61
- def unqualified_name
62
- name_nesting.last
36
+ def match_expressions
37
+ scope.match_expressions
63
38
  end
64
39
 
65
- # Match expressions for scope
40
+ # Return root node for mutation
66
41
  #
67
- # @return [Enumerable<Expression>]
68
- def match_expressions
69
- name_nesting.each_index.reverse_each.map do |index|
70
- Expression::Namespace::Recursive.new(
71
- scope_name: name_nesting.take(index.succ).join(NAMESPACE_DELIMITER)
72
- )
73
- end
42
+ # @return [Parser::AST::Node]
43
+ def root(node)
44
+ constant_scope.call(node)
74
45
  end
75
- memoize :match_expressions
76
46
 
77
- private
78
-
79
- def name_nesting
80
- scope.name.split(NAMESPACE_DELIMITER)
47
+ # Identification string
48
+ #
49
+ # @return [String]
50
+ def identification
51
+ scope.raw.name
81
52
  end
82
- memoize :name_nesting
83
53
 
84
54
  end # Context
85
55
  end # Mutant
data/lib/mutant/env.rb CHANGED
@@ -66,10 +66,18 @@ module Mutant
66
66
  )
67
67
  end
68
68
 
69
+ def run_test_index(test_index)
70
+ integration.call([integration.all_tests.fetch(test_index)])
71
+ end
72
+
69
73
  def emit_mutation_worker_process_start(index:)
70
74
  hooks.run(:mutation_worker_process_start, index: index)
71
75
  end
72
76
 
77
+ def emit_test_worker_process_start(index:)
78
+ hooks.run(:test_worker_process_start, index: index)
79
+ end
80
+
73
81
  # The test selections
74
82
  #
75
83
  # @return Hash{Mutation => Enumerable<Test>}
@@ -175,7 +183,6 @@ module Mutant
175
183
  def timer
176
184
  world.timer
177
185
  end
178
-
179
186
  end # Env
180
187
  # rubocop:enable Metrics/ClassLength
181
188
  end # Mutant
@@ -78,7 +78,10 @@ module Mutant
78
78
  private
79
79
 
80
80
  def scope
81
- Object.const_get(scope_name)
81
+ Scope.new(
82
+ raw: Object.const_get(scope_name),
83
+ expression: Namespace::Exact.new(scope_name: scope_name)
84
+ )
82
85
  end
83
86
 
84
87
  end # Method
@@ -57,7 +57,10 @@ module Mutant
57
57
  private
58
58
 
59
59
  def scope
60
- Object.const_get(scope_name)
60
+ Scope.new(
61
+ expression: Namespace::Exact.new(scope_name: scope_name),
62
+ raw: Object.const_get(scope_name)
63
+ )
61
64
  end
62
65
 
63
66
  end # Methods
@@ -66,10 +66,10 @@ module Mutant
66
66
  #
67
67
  # @return [Matcher]
68
68
  def matcher
69
- scope = find_scope
69
+ raw_scope = find_raw_scope
70
70
 
71
- if scope
72
- Matcher::Scope.new(scope: scope)
71
+ if raw_scope
72
+ Matcher::Scope.new(scope: Scope.new(expression: self, raw: raw_scope))
73
73
  else
74
74
  Matcher::Null.new
75
75
  end
@@ -83,7 +83,7 @@ module Mutant
83
83
 
84
84
  private
85
85
 
86
- def find_scope
86
+ def find_raw_scope
87
87
  Object.const_get(scope_name)
88
88
  rescue NameError # rubocop:disable Lint/SuppressedException
89
89
  end
data/lib/mutant/hooks.rb CHANGED
@@ -10,6 +10,7 @@ module Mutant
10
10
  mutation_insert_post
11
11
  mutation_insert_pre
12
12
  mutation_worker_process_start
13
+ test_worker_process_start
13
14
  ].product([EMPTY_ARRAY]).to_h.transform_values(&:freeze).freeze
14
15
 
15
16
  MESSAGE = 'Unknown hook %s'
@@ -18,6 +18,7 @@ module Mutant
18
18
  # @return [Result::Test]
19
19
  def call(_tests)
20
20
  Result::Test.new(
21
+ output: '',
21
22
  passed: true,
22
23
  runtime: 0.0
23
24
  )
@@ -4,7 +4,11 @@ module Mutant
4
4
 
5
5
  # Abstract base class mutant test framework integrations
6
6
  class Integration
7
- include AbstractType, Adamantium, Anima.new(:arguments, :expression_parser, :world)
7
+ include AbstractType, Adamantium, Anima.new(
8
+ :arguments,
9
+ :expression_parser,
10
+ :world
11
+ )
8
12
 
9
13
  LOAD_MESSAGE = <<~'MESSAGE'
10
14
  Unable to load integration mutant-%<integration_name>s:
@@ -10,7 +10,7 @@ module Mutant
10
10
  const = env.world.try_const_get(const_name) or return EMPTY_ARRAY
11
11
 
12
12
  Chain.new(
13
- matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope.raw) }
13
+ matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope) }
14
14
  ).call(env)
15
15
  end
16
16
 
@@ -8,7 +8,7 @@ module Mutant
8
8
 
9
9
  # Dispatching builder, detects memoizable case
10
10
  #
11
- # @param [Class, Module] scope
11
+ # @param [Scope] scope
12
12
  # @param [UnboundMethod] method
13
13
  #
14
14
  # @return [Matcher::Method::Instance]
@@ -31,7 +31,7 @@ module Mutant
31
31
  # rubocop:enable Metrics/MethodLength
32
32
 
33
33
  def self.memoized_method?(scope, method_name)
34
- scope < Adamantium && scope.memoized?(method_name)
34
+ scope.raw < Adamantium && scope.raw.memoized?(method_name)
35
35
  end
36
36
  private_class_method :memoized_method?
37
37
 
@@ -48,9 +48,9 @@ module Mutant
48
48
  end
49
49
 
50
50
  def visibility
51
- if scope.private_instance_methods.include?(method_name)
51
+ if scope.raw.private_instance_methods.include?(method_name)
52
52
  :private
53
- elsif scope.protected_instance_methods.include?(method_name)
53
+ elsif scope.raw.protected_instance_methods.include?(method_name)
54
54
  :protected
55
55
  else
56
56
  :public
@@ -65,6 +65,7 @@ module Mutant
65
65
 
66
66
  def source_location
67
67
  scope
68
+ .raw
68
69
  .unmemoized_instance_method(method_name)
69
70
  .source_location
70
71
  end
@@ -62,7 +62,7 @@ module Mutant
62
62
 
63
63
  def sclass_const_name?(node)
64
64
  name = node.children.fetch(CONST_NAME_INDEX)
65
- name.to_s.eql?(context.unqualified_name)
65
+ name.to_s.eql?(scope.unqualified_name)
66
66
  end
67
67
 
68
68
  end # Evaluator
@@ -46,7 +46,7 @@ module Mutant
46
46
 
47
47
  def receiver_name?(node)
48
48
  name = node.children.fetch(NAME_INDEX)
49
- name.to_s.eql?(context.unqualified_name)
49
+ name.to_s.eql?(scope.unqualified_name)
50
50
  end
51
51
 
52
52
  end # Evaluator
@@ -14,6 +14,11 @@ module Mutant
14
14
  CLOSURE_WARNING_FORMAT =
15
15
  '%s is dynamically defined in a closure, unable to emit subject'
16
16
 
17
+ CONSTANT_SCOPES = {
18
+ class: Context::ConstantScope::Class,
19
+ module: Context::ConstantScope::Module
20
+ }.freeze
21
+
17
22
  # Matched subjects
18
23
  #
19
24
  # @param [Env] env
@@ -28,6 +33,8 @@ module Mutant
28
33
  # Present to avoid passing the env argument around in case the
29
34
  # logic would be implemented directly on the Matcher::Method
30
35
  # instance
36
+ #
37
+ # rubocop:disable Metrics/ClassLength
31
38
  class Evaluator
32
39
  include(
33
40
  AbstractType,
@@ -57,7 +64,7 @@ module Mutant
57
64
  def match_view
58
65
  return EMPTY_ARRAY if matched_view.nil?
59
66
 
60
- if matched_view.path.any?(&:block.public_method(:equal?))
67
+ if matched_view.stack.any? { |node| node.type.equal?(:block) }
61
68
  env.warn(CLOSURE_WARNING_FORMAT % target_method)
62
69
 
63
70
  return EMPTY_ARRAY
@@ -80,7 +87,26 @@ module Mutant
80
87
  end
81
88
 
82
89
  def context
83
- Context.new(scope: scope, source_path: source_path)
90
+ Context.new(constant_scope: constant_scope, scope: scope, source_path: source_path)
91
+ end
92
+
93
+ # rubocop:disable Metrics/MethodLength
94
+ def constant_scope
95
+ matched_view
96
+ .stack
97
+ .reverse
98
+ .reduce(Context::ConstantScope::None.new) do |descendant, node|
99
+ klass = CONSTANT_SCOPES[node.type]
100
+
101
+ if klass
102
+ klass.new(
103
+ const: node.children.fetch(0),
104
+ descendant: descendant
105
+ )
106
+ else
107
+ descendant
108
+ end
109
+ end
84
110
  end
85
111
 
86
112
  def ast
@@ -151,9 +177,9 @@ module Mutant
151
177
  # end
152
178
  #
153
179
  # Change to this once 3.0 is EOL.
154
- if scope.private_methods.include?(method_name)
180
+ if scope.raw.private_methods.include?(method_name)
155
181
  :private
156
- elsif scope.protected_methods.include?(method_name)
182
+ elsif scope.raw.protected_methods.include?(method_name)
157
183
  :protected
158
184
  else
159
185
  :public
@@ -57,11 +57,11 @@ module Mutant
57
57
  private
58
58
 
59
59
  def access(_env, method_name)
60
- scope.method(method_name)
60
+ scope.raw.method(method_name)
61
61
  end
62
62
 
63
63
  def candidate_scope
64
- scope.singleton_class
64
+ scope.raw.singleton_class
65
65
  end
66
66
 
67
67
  end # Singleton
@@ -73,11 +73,11 @@ module Mutant
73
73
  private
74
74
 
75
75
  def access(_env, method_name)
76
- scope.method(method_name)
76
+ scope.raw.method(method_name)
77
77
  end
78
78
 
79
79
  def candidate_scope
80
- scope.singleton_class
80
+ scope.raw.singleton_class
81
81
  end
82
82
  end # Metaclass
83
83
 
@@ -105,18 +105,19 @@ module Mutant
105
105
  private
106
106
 
107
107
  # rubocop:disable Lint/RescueException
108
+ # mutant:disable - unstable source locations under < ruby-3.2
108
109
  def access(env, method_name)
109
- scope.instance_method(method_name)
110
+ candidate_scope.instance_method(method_name)
110
111
  rescue Exception => exception
111
112
  env.warn(
112
- MESSAGE % { scope: scope, method_name: method_name, exception: exception }
113
+ MESSAGE % { scope: scope, method_name: method_name, exception: exception.inspect }
113
114
  )
114
115
  nil
115
116
  end
116
117
  # rubocop:enable Lint/RescueException
117
118
 
118
119
  def candidate_scope
119
- scope
120
+ scope.raw
120
121
  end
121
122
 
122
123
  end # Instance
@@ -13,7 +13,7 @@ module Mutant
13
13
  # @return [Enumerable<Subject>]
14
14
  def call(env)
15
15
  Chain.new(
16
- matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope.raw) }
16
+ matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope) }
17
17
  ).call(env)
18
18
  end
19
19