mutant 0.11.28 → 0.11.30
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.
- checksums.yaml +4 -4
- data/lib/mutant/ast.rb +8 -9
- data/lib/mutant/bootstrap.rb +45 -13
- data/lib/mutant/cli/command/environment/test.rb +38 -1
- data/lib/mutant/cli/command/environment.rb +11 -0
- data/lib/mutant/context.rb +31 -61
- data/lib/mutant/env.rb +8 -1
- data/lib/mutant/expression/method.rb +4 -1
- data/lib/mutant/expression/methods.rb +4 -1
- data/lib/mutant/expression/namespace.rb +4 -4
- data/lib/mutant/hooks.rb +3 -0
- data/lib/mutant/integration/null.rb +1 -0
- data/lib/mutant/integration.rb +5 -1
- data/lib/mutant/matcher/descendants.rb +1 -1
- data/lib/mutant/matcher/method/instance.rb +5 -4
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/matcher/method/singleton.rb +1 -1
- data/lib/mutant/matcher/method.rb +30 -4
- data/lib/mutant/matcher/methods.rb +8 -7
- data/lib/mutant/matcher/namespace.rb +1 -1
- data/lib/mutant/meta/example.rb +12 -2
- data/lib/mutant/mutation/runner/sink.rb +7 -3
- data/lib/mutant/mutation/runner.rb +2 -3
- data/lib/mutant/parallel/connection.rb +178 -0
- data/lib/mutant/parallel/pipe.rb +39 -0
- data/lib/mutant/parallel/worker.rb +42 -14
- data/lib/mutant/parallel.rb +18 -7
- data/lib/mutant/reporter/cli/format.rb +19 -2
- data/lib/mutant/reporter/cli/printer/test.rb +138 -0
- data/lib/mutant/reporter/cli.rb +33 -4
- data/lib/mutant/reporter.rb +22 -1
- data/lib/mutant/result.rb +53 -2
- data/lib/mutant/scope.rb +41 -1
- data/lib/mutant/subject/method/instance.rb +3 -2
- data/lib/mutant/subject/method/metaclass.rb +1 -1
- data/lib/mutant/subject/method/singleton.rb +2 -2
- data/lib/mutant/subject/method.rb +1 -1
- data/lib/mutant/test/runner/sink.rb +51 -0
- data/lib/mutant/test/runner.rb +62 -0
- data/lib/mutant/timer.rb +9 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +2 -0
- data/lib/mutant.rb +7 -1
- metadata +9 -19
- data/lib/mutant/pipe.rb +0 -96
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c9ce21c5e092af5e521c5ef2c0e926e60211ddae0c8b552d5b79180dbc8eb6c
|
|
4
|
+
data.tar.gz: 994dbc55bb97edd121d72aa46a946993e2efcc8642ca0d7dd7d31cdb4cb8fa6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7047e6694559bb810a7b3b7bc1f01e07745a928c166e6964f0b74d8dae3a4900575bbe12bcf98fc0163bef3ee6869fd2b09ec09b46d91920b3de134421811240
|
|
7
|
+
data.tar.gz: cf40b6c4d5c5a4ea37a090f7a874e3a2abfd64ea31d0d2d1c5ae32cb6e8b43c2d4b5c7c8c92be24a54339be87604f66c59a7d5f11061299518cf93fdb1da2e44
|
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, :
|
|
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,
|
|
16
|
-
View.new(node: node,
|
|
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,
|
|
25
|
+
walk_path(node, []) do |node, stack|
|
|
26
26
|
expression = node.location.expression || next
|
|
27
|
-
(line_map[expression.line] ||= []) << [node,
|
|
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
|
|
35
|
-
block.call(node, stack
|
|
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
|
data/lib/mutant/bootstrap.rb
CHANGED
|
@@ -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: %<
|
|
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,36 @@ 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
|
+
# rubocop:disable Metrics/MethodLength
|
|
75
|
+
# rubocop:disable Style/MultilineBlockChain
|
|
76
|
+
def self.setup_integration(env:, mutations:, selected_subjects:)
|
|
77
|
+
env.record(__method__) do
|
|
78
|
+
hooks = env.hooks
|
|
79
|
+
hooks.run(:setup_integration_pre)
|
|
50
80
|
Integration.setup(env).fmap do |integration|
|
|
51
81
|
env.with(
|
|
52
82
|
integration: integration,
|
|
@@ -54,10 +84,12 @@ module Mutant
|
|
|
54
84
|
selector: Selector::Expression.new(integration: integration),
|
|
55
85
|
subjects: selected_subjects
|
|
56
86
|
)
|
|
57
|
-
end
|
|
87
|
+
end.tap { hooks.run(:setup_integration_post) }
|
|
58
88
|
end
|
|
59
89
|
end
|
|
90
|
+
private_class_method :setup_integration
|
|
60
91
|
# rubocop:enable Metrics/MethodLength
|
|
92
|
+
# rubocop:enable Style/MultilineBlockChain
|
|
61
93
|
|
|
62
94
|
def self.load_hooks(env)
|
|
63
95
|
env.record(__method__) do
|
|
@@ -113,9 +145,9 @@ module Mutant
|
|
|
113
145
|
env.record(__method__) do
|
|
114
146
|
config = env.config
|
|
115
147
|
|
|
116
|
-
scopes = env.world.object_space.each_object(Module).with_object([]) do |
|
|
117
|
-
expression = expression(config.reporter, config.expression_parser,
|
|
118
|
-
aggregate << Scope.new(raw:
|
|
148
|
+
scopes = env.world.object_space.each_object(Module).with_object([]) do |raw_scope, aggregate|
|
|
149
|
+
expression = expression(config.reporter, config.expression_parser, raw_scope) || next
|
|
150
|
+
aggregate << Scope.new(raw: raw_scope, expression: expression)
|
|
119
151
|
end
|
|
120
152
|
|
|
121
153
|
scopes.sort_by { |scope| scope.expression.syntax }
|
|
@@ -123,31 +155,31 @@ module Mutant
|
|
|
123
155
|
end
|
|
124
156
|
private_class_method :matchable_scopes
|
|
125
157
|
|
|
126
|
-
def self.scope_name(reporter,
|
|
127
|
-
|
|
158
|
+
def self.scope_name(reporter, raw_scope)
|
|
159
|
+
raw_scope.name
|
|
128
160
|
rescue => exception
|
|
129
161
|
semantics_warning(
|
|
130
162
|
reporter,
|
|
131
163
|
CLASS_NAME_RAISED_EXCEPTION,
|
|
132
164
|
exception: exception.inspect,
|
|
133
|
-
scope:
|
|
134
|
-
scope_class:
|
|
165
|
+
scope: raw_scope,
|
|
166
|
+
scope_class: raw_scope.class
|
|
135
167
|
)
|
|
136
168
|
nil
|
|
137
169
|
end
|
|
138
170
|
private_class_method :scope_name
|
|
139
171
|
|
|
140
172
|
# rubocop:disable Metrics/MethodLength
|
|
141
|
-
def self.expression(reporter, expression_parser,
|
|
142
|
-
name = scope_name(reporter,
|
|
173
|
+
def self.expression(reporter, expression_parser, raw_scope)
|
|
174
|
+
name = scope_name(reporter, raw_scope) or return
|
|
143
175
|
|
|
144
176
|
unless name.instance_of?(String)
|
|
145
177
|
semantics_warning(
|
|
146
178
|
reporter,
|
|
147
179
|
CLASS_NAME_TYPE_MISMATCH_FORMAT,
|
|
148
180
|
name: name,
|
|
149
|
-
scope_class:
|
|
150
|
-
|
|
181
|
+
scope_class: raw_scope.class,
|
|
182
|
+
raw_scope: raw_scope
|
|
151
183
|
)
|
|
152
184
|
return
|
|
153
185
|
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
|
-
|
|
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
|
data/lib/mutant/context.rb
CHANGED
|
@@ -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
|
-
|
|
8
|
+
class ConstantScope
|
|
9
|
+
include AST::Sexp
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
40
|
+
# Return root node for mutation
|
|
66
41
|
#
|
|
67
|
-
# @return [
|
|
68
|
-
def
|
|
69
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
@@ -66,10 +66,10 @@ module Mutant
|
|
|
66
66
|
#
|
|
67
67
|
# @return [Matcher]
|
|
68
68
|
def matcher
|
|
69
|
-
|
|
69
|
+
raw_scope = find_raw_scope
|
|
70
70
|
|
|
71
|
-
if
|
|
72
|
-
Matcher::Scope.new(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
|
|
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,9 @@ module Mutant
|
|
|
10
10
|
mutation_insert_post
|
|
11
11
|
mutation_insert_pre
|
|
12
12
|
mutation_worker_process_start
|
|
13
|
+
setup_integration_post
|
|
14
|
+
setup_integration_pre
|
|
15
|
+
test_worker_process_start
|
|
13
16
|
].product([EMPTY_ARRAY]).to_h.transform_values(&:freeze).freeze
|
|
14
17
|
|
|
15
18
|
MESSAGE = 'Unknown hook %s'
|
data/lib/mutant/integration.rb
CHANGED
|
@@ -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(
|
|
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
|
|
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 [
|
|
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
|
|
@@ -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.
|
|
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
|
-
|
|
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
|