mutant 0.11.27 → 0.11.29
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 +39 -13
- data/lib/mutant/cli/command/environment/test.rb +38 -1
- data/lib/mutant/cli/command/environment.rb +15 -0
- data/lib/mutant/cli/command.rb +10 -6
- 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 +1 -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 -1
- data/lib/mutant/mutator/node/literal/hash.rb +3 -1
- 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/env_progress.rb +3 -4
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -1
- 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 -13
- 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 +6 -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: 3e57360ec50ffdef54f132f4764c940caad83a2d3692ac812b95c236cd8ff52d
|
|
4
|
+
data.tar.gz: cc69f0948238d888f569caeee901dc9197b620a3693474323a0959212d73fc30
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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, :
|
|
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,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
|
-
|
|
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 |
|
|
117
|
-
expression = expression(config.reporter, config.expression_parser,
|
|
118
|
-
aggregate << Scope.new(raw:
|
|
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,
|
|
127
|
-
|
|
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:
|
|
134
|
-
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,
|
|
142
|
-
name = scope_name(reporter,
|
|
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:
|
|
150
|
-
|
|
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
|
-
|
|
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
|
|
@@ -56,6 +58,10 @@ module Mutant
|
|
|
56
58
|
set(matcher: @config.matcher.add(attribute, value))
|
|
57
59
|
end
|
|
58
60
|
|
|
61
|
+
def effective_options
|
|
62
|
+
instance_of?(Environment) ? EMPTY_ARRAY : super()
|
|
63
|
+
end
|
|
64
|
+
|
|
59
65
|
# rubocop:disable Metrics/MethodLength
|
|
60
66
|
def add_environment_options(parser)
|
|
61
67
|
parser.separator('Environment:')
|
|
@@ -122,7 +128,16 @@ module Mutant
|
|
|
122
128
|
set(mutation: @config.mutation.with(timeout: Float(number)))
|
|
123
129
|
end
|
|
124
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
|
|
125
139
|
end # Run
|
|
140
|
+
# rubocop:enable Metrics/ClassLength
|
|
126
141
|
end # Command
|
|
127
142
|
end # CLI
|
|
128
143
|
end # Mutant
|
data/lib/mutant/cli/command.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Mutant
|
|
|
6
6
|
class Command
|
|
7
7
|
include AbstractType, Anima.new(
|
|
8
8
|
:main,
|
|
9
|
-
:
|
|
9
|
+
:parent_names,
|
|
10
10
|
:print_profile,
|
|
11
11
|
:world,
|
|
12
12
|
:zombie
|
|
@@ -27,10 +27,10 @@ module Mutant
|
|
|
27
27
|
# @return [Command]
|
|
28
28
|
#
|
|
29
29
|
# rubocop:disable Metrics/ParameterLists
|
|
30
|
-
def self.parse(arguments:,
|
|
30
|
+
def self.parse(arguments:, parent_names: nil, print_profile: false, world:, zombie: false)
|
|
31
31
|
new(
|
|
32
32
|
main: nil,
|
|
33
|
-
|
|
33
|
+
parent_names: parent_names,
|
|
34
34
|
print_profile: print_profile,
|
|
35
35
|
world: world,
|
|
36
36
|
zombie: zombie
|
|
@@ -63,7 +63,7 @@ module Mutant
|
|
|
63
63
|
#
|
|
64
64
|
# @return [String]
|
|
65
65
|
def full_name
|
|
66
|
-
[*
|
|
66
|
+
[*parent_names, self.class.command_name].join(' ')
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
alias_method :print_profile?, :print_profile
|
|
@@ -97,13 +97,17 @@ module Mutant
|
|
|
97
97
|
add_global_options(parser)
|
|
98
98
|
add_subcommands(parser)
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
effective_options.each do |method_name|
|
|
101
101
|
2.times { parser.separator(nil) }
|
|
102
102
|
__send__(method_name, parser)
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
def effective_options
|
|
108
|
+
self.class::OPTIONS
|
|
109
|
+
end
|
|
110
|
+
|
|
107
111
|
def capture_main(&block)
|
|
108
112
|
@main = block
|
|
109
113
|
end
|
|
@@ -189,7 +193,7 @@ module Mutant
|
|
|
189
193
|
find_command(command_name).bind do |command|
|
|
190
194
|
command.parse(
|
|
191
195
|
arguments: arguments,
|
|
192
|
-
|
|
196
|
+
parent_names: [*parent_names, self.class::NAME],
|
|
193
197
|
print_profile: print_profile,
|
|
194
198
|
world: world,
|
|
195
199
|
zombie: zombie
|
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
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
|