mutant 0.5.23 → 0.5.24
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/Changelog.md +10 -0
- data/config/flay.yml +1 -1
- data/config/reek.yml +19 -19
- data/lib/mutant.rb +12 -39
- data/lib/mutant/ast.rb +5 -0
- data/lib/mutant/ast/meta.rb +131 -0
- data/lib/mutant/ast/named_children.rb +98 -0
- data/lib/mutant/ast/node_predicates.rb +19 -0
- data/lib/mutant/ast/nodes.rb +21 -0
- data/lib/mutant/ast/sexp.rb +34 -0
- data/lib/mutant/ast/types.rb +48 -0
- data/lib/mutant/cache.rb +3 -2
- data/lib/mutant/cli.rb +11 -151
- data/lib/mutant/config.rb +22 -2
- data/lib/mutant/context/scope.rb +11 -21
- data/lib/mutant/delegator.rb +2 -0
- data/lib/mutant/diff.rb +7 -3
- data/lib/mutant/env.rb +49 -0
- data/lib/mutant/expression.rb +36 -8
- data/lib/mutant/expression/methods.rb +62 -0
- data/lib/mutant/expression/namespace.rb +41 -28
- data/lib/mutant/{strategy.rb → integration.rb} +12 -31
- data/lib/mutant/isolation.rb +1 -1
- data/lib/mutant/matcher.rb +1 -21
- data/lib/mutant/matcher/builder.rb +142 -0
- data/lib/mutant/matcher/method.rb +3 -7
- data/lib/mutant/matcher/method/instance.rb +6 -5
- data/lib/mutant/matcher/method/singleton.rb +2 -7
- data/lib/mutant/matcher/methods.rb +11 -14
- data/lib/mutant/matcher/namespace.rb +31 -39
- data/lib/mutant/matcher/scope.rb +13 -2
- data/lib/mutant/meta.rb +0 -1
- data/lib/mutant/meta/example/dsl.rb +5 -1
- data/lib/mutant/mutator/node.rb +16 -44
- data/lib/mutant/mutator/node/or_asgn.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +5 -60
- data/lib/mutant/mutator/node/super.rb +2 -5
- data/lib/mutant/mutator/registry.rb +1 -1
- data/lib/mutant/reporter.rb +10 -0
- data/lib/mutant/reporter/cli.rb +13 -0
- data/lib/mutant/reporter/cli/printer.rb +2 -0
- data/lib/mutant/reporter/cli/progress/config.rb +1 -1
- data/lib/mutant/reporter/cli/progress/noop.rb +2 -0
- data/lib/mutant/reporter/cli/registry.rb +2 -0
- data/lib/mutant/reporter/null.rb +12 -0
- data/lib/mutant/reporter/trace.rb +4 -0
- data/lib/mutant/require_highjack.rb +2 -2
- data/lib/mutant/rspec.rb +0 -0
- data/lib/mutant/runner.rb +2 -0
- data/lib/mutant/runner/config.rb +8 -8
- data/lib/mutant/runner/killer.rb +5 -0
- data/lib/mutant/runner/subject.rb +1 -1
- data/lib/mutant/subject.rb +8 -8
- data/lib/mutant/subject/method.rb +3 -2
- data/lib/mutant/subject/method/instance.rb +1 -1
- data/lib/mutant/test.rb +7 -65
- data/lib/mutant/test/report.rb +59 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warning_filter.rb +2 -0
- data/lib/mutant/zombifier.rb +3 -0
- data/lib/mutant/zombifier/file.rb +1 -1
- data/meta/or_asgn.rb +11 -0
- data/meta/send.rb +1 -1
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/corpus_spec.rb +2 -0
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -3
- data/spec/unit/mutant/cli_new_spec.rb +11 -11
- data/spec/unit/mutant/expression/methods_spec.rb +43 -0
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -1
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +19 -5
- data/spec/unit/mutant/{strategy_spec.rb → integration_spec.rb} +1 -1
- data/spec/unit/mutant/isolation_spec.rb +3 -1
- data/spec/unit/mutant/matcher/method/instance_spec.rb +5 -5
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +8 -8
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +5 -8
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +5 -5
- data/spec/unit/mutant/matcher/namespace_spec.rb +18 -23
- data/spec/unit/mutant/mutation_spec.rb +1 -1
- data/spec/unit/mutant/runner/config_spec.rb +4 -5
- data/spec/unit/mutant/runner/mutation_spec.rb +21 -21
- data/spec/unit/mutant/runner/subject_spec.rb +6 -6
- data/spec/unit/mutant/subject/method/instance_spec.rb +0 -4
- data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -1
- data/spec/unit/mutant/subject_spec.rb +3 -3
- metadata +20 -6
- data/lib/mutant/node_helpers.rb +0 -52
data/lib/mutant/delegator.rb
CHANGED
data/lib/mutant/diff.rb
CHANGED
@@ -3,6 +3,10 @@ module Mutant
|
|
3
3
|
class Diff
|
4
4
|
include Adamantium::Flat, Concord.new(:old, :new)
|
5
5
|
|
6
|
+
ADDITION = '+'.freeze
|
7
|
+
DELETION = '-'.freeze
|
8
|
+
NEWLINE = "\n".freeze
|
9
|
+
|
6
10
|
# Return source diff
|
7
11
|
#
|
8
12
|
# @return [String]
|
@@ -16,7 +20,7 @@ module Mutant
|
|
16
20
|
def diff
|
17
21
|
return unless diffs.length.equal?(1)
|
18
22
|
::Diff::LCS::Hunk.new(old, new, diffs.first, max_length, 0)
|
19
|
-
.diff(:unified) <<
|
23
|
+
.diff(:unified) << NEWLINE
|
20
24
|
end
|
21
25
|
memoize :diff
|
22
26
|
|
@@ -97,9 +101,9 @@ module Mutant
|
|
97
101
|
#
|
98
102
|
def self.colorize_line(line)
|
99
103
|
case line[0]
|
100
|
-
when
|
104
|
+
when ADDITION
|
101
105
|
Color::GREEN
|
102
|
-
when
|
106
|
+
when DELETION
|
103
107
|
Color::RED
|
104
108
|
else
|
105
109
|
Color::NONE
|
data/lib/mutant/env.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Abstract base class for mutant environments
|
3
|
+
class Env
|
4
|
+
include AbstractType, Adamantium
|
5
|
+
|
6
|
+
# Return config
|
7
|
+
#
|
8
|
+
# @return [Config]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
abstract_method :config
|
13
|
+
|
14
|
+
# Return cache
|
15
|
+
#
|
16
|
+
# @return [Cache]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
abstract_method :cache
|
21
|
+
|
22
|
+
# Return reporter
|
23
|
+
#
|
24
|
+
# @return [Reporter]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
#
|
28
|
+
abstract_method :reporter
|
29
|
+
|
30
|
+
# Print warning message
|
31
|
+
#
|
32
|
+
# @param [String]
|
33
|
+
#
|
34
|
+
# @return [self]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def warn(message)
|
39
|
+
reporter.warn(message)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Boot environment used for matching
|
44
|
+
class Boot < self
|
45
|
+
include Concord::Public.new(:reporter, :cache)
|
46
|
+
end # Boot
|
47
|
+
|
48
|
+
end # ENV
|
49
|
+
end # Mutant
|
data/lib/mutant/expression.rb
CHANGED
@@ -10,7 +10,7 @@ module Mutant
|
|
10
10
|
|
11
11
|
METHOD_NAME_PATTERN = Regexp.union(
|
12
12
|
/[A-Za-z_][A-Za-z\d_]*[!?=]?/,
|
13
|
-
*OPERATOR_METHODS.map(&:to_s)
|
13
|
+
*AST::Types::OPERATOR_METHODS.map(&:to_s)
|
14
14
|
).freeze
|
15
15
|
|
16
16
|
SCOPE_PATTERN = /#{SCOPE_NAME_PATTERN}(?:#{SCOPE_OPERATOR}#{SCOPE_NAME_PATTERN})*/.freeze
|
@@ -38,20 +38,32 @@ module Mutant
|
|
38
38
|
|
39
39
|
# Return match length for expression
|
40
40
|
#
|
41
|
-
# @param [Expression]
|
41
|
+
# @param [Expression] other
|
42
42
|
#
|
43
43
|
# @return [Fixnum]
|
44
44
|
#
|
45
45
|
# @api private
|
46
46
|
#
|
47
|
-
def match_length(
|
48
|
-
if eql?(
|
47
|
+
def match_length(other)
|
48
|
+
if eql?(other)
|
49
49
|
syntax.length
|
50
50
|
else
|
51
51
|
0
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
# Test if expression is prefix
|
56
|
+
#
|
57
|
+
# @param [Expression] other
|
58
|
+
#
|
59
|
+
# @return [Boolean]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
def prefix?(other)
|
64
|
+
!match_length(other).zero?
|
65
|
+
end
|
66
|
+
|
55
67
|
# Register expression
|
56
68
|
#
|
57
69
|
# @return [undefined]
|
@@ -63,9 +75,25 @@ module Mutant
|
|
63
75
|
end
|
64
76
|
private_class_method :register
|
65
77
|
|
78
|
+
# Parse input into expression or raise
|
79
|
+
#
|
80
|
+
# @param [String] syntax
|
81
|
+
#
|
82
|
+
# @return [Expression]
|
83
|
+
# if expression is valid
|
84
|
+
#
|
85
|
+
# @raise [RuntimeError]
|
86
|
+
# otherwise
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
#
|
90
|
+
def self.parse(input)
|
91
|
+
try_parse(input) or raise "Expression: #{input.inspect} is not valid"
|
92
|
+
end
|
93
|
+
|
66
94
|
# Parse input into expression
|
67
95
|
#
|
68
|
-
# @param [String]
|
96
|
+
# @param [String] input
|
69
97
|
#
|
70
98
|
# @return [Expression]
|
71
99
|
# if expression is valid
|
@@ -75,14 +103,14 @@ module Mutant
|
|
75
103
|
#
|
76
104
|
# @api private
|
77
105
|
#
|
78
|
-
def self.
|
79
|
-
expressions = expressions(
|
106
|
+
def self.try_parse(input)
|
107
|
+
expressions = expressions(input)
|
80
108
|
case expressions.length
|
81
109
|
when 0
|
82
110
|
when 1
|
83
111
|
expressions.first
|
84
112
|
else
|
85
|
-
fail "Ambigous expression: #{
|
113
|
+
fail "Ambigous expression: #{input.inspect}"
|
86
114
|
end
|
87
115
|
end
|
88
116
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Expression
|
3
|
+
|
4
|
+
# Abstrat base class for methods expression
|
5
|
+
class Methods < self
|
6
|
+
|
7
|
+
MATCHERS = {
|
8
|
+
'.' => Matcher::Methods::Singleton,
|
9
|
+
'#' => Matcher::Methods::Instance
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
register(
|
13
|
+
/\A(?<scope_name>#{SCOPE_PATTERN})(?<scope_symbol>[.#])\z/
|
14
|
+
)
|
15
|
+
|
16
|
+
# Return method matcher
|
17
|
+
#
|
18
|
+
# @param [Cache] cache
|
19
|
+
#
|
20
|
+
# @return [Matcher::Method]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
def matcher(cache)
|
25
|
+
MATCHERS.fetch(scope_symbol).new(cache, scope)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Return scope name
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def scope_name
|
37
|
+
match[__method__]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return scope
|
41
|
+
#
|
42
|
+
# @return [Class, Method]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
#
|
46
|
+
def scope
|
47
|
+
Mutant.constant_lookup(scope_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return scope symbol
|
51
|
+
#
|
52
|
+
# @return [Symbol]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
#
|
56
|
+
def scope_symbol
|
57
|
+
match[__method__]
|
58
|
+
end
|
59
|
+
|
60
|
+
end # Method
|
61
|
+
end # Expression
|
62
|
+
end # Mutant
|
@@ -4,37 +4,11 @@ module Mutant
|
|
4
4
|
class Namespace < self
|
5
5
|
include AbstractType
|
6
6
|
|
7
|
-
# Return matcher
|
8
|
-
#
|
9
|
-
# @param [Cache] cache
|
10
|
-
#
|
11
|
-
# @return [Matcher]
|
12
|
-
#
|
13
|
-
# @api private
|
14
|
-
#
|
15
|
-
def matcher(cache)
|
16
|
-
self.class::MATCHER.new(cache, namespace)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
# Return namespace
|
22
|
-
#
|
23
|
-
# @return [Class, Module]
|
24
|
-
#
|
25
|
-
# @api private
|
26
|
-
#
|
27
|
-
def namespace
|
28
|
-
Mutant.constant_lookup(match[__method__].to_s)
|
29
|
-
end
|
30
|
-
|
31
7
|
# Recursive namespace expression
|
32
8
|
class Recursive < self
|
33
9
|
|
34
10
|
register(/\A(?<namespace>#{SCOPE_PATTERN})?\*\z/)
|
35
11
|
|
36
|
-
MATCHER = Matcher::Namespace
|
37
|
-
|
38
12
|
# Initialize object
|
39
13
|
#
|
40
14
|
# @return [undefined]
|
@@ -43,7 +17,23 @@ module Mutant
|
|
43
17
|
def initialize(*)
|
44
18
|
super
|
45
19
|
namespace_src = Regexp.escape(namespace)
|
46
|
-
@recursion_pattern = Regexp.union(
|
20
|
+
@recursion_pattern = Regexp.union(
|
21
|
+
/\A#{namespace_src}\z/,
|
22
|
+
/\A#{namespace_src}::/,
|
23
|
+
/\A#{namespace_src}[.#]/
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return matcher
|
28
|
+
#
|
29
|
+
# @param [Cache] cache
|
30
|
+
#
|
31
|
+
# @return [Matcher]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
def matcher(cache)
|
36
|
+
Matcher::Namespace.new(cache, self)
|
47
37
|
end
|
48
38
|
|
49
39
|
# Return length of match
|
@@ -83,8 +73,31 @@ module Mutant
|
|
83
73
|
|
84
74
|
MATCHER = Matcher::Scope
|
85
75
|
|
86
|
-
|
76
|
+
# Return matcher
|
77
|
+
#
|
78
|
+
# @param [Cache] cache
|
79
|
+
#
|
80
|
+
# @return [Matcher]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
#
|
84
|
+
def matcher(cache)
|
85
|
+
Matcher::Scope.new(cache, Mutant.constant_lookup(namespace))
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Return namespace
|
91
|
+
#
|
92
|
+
# @return [String]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
#
|
96
|
+
def namespace
|
97
|
+
match[__method__].to_s
|
98
|
+
end
|
87
99
|
|
100
|
+
end # Exact
|
88
101
|
end # Namespace
|
89
102
|
end # Namespace
|
90
103
|
end # Mutant
|
@@ -1,16 +1,16 @@
|
|
1
1
|
module Mutant
|
2
2
|
|
3
|
-
# Abstract base class
|
4
|
-
class
|
3
|
+
# Abstract base class mutant test framework integrations
|
4
|
+
class Integration
|
5
5
|
include AbstractType, Adamantium::Flat, Equalizer.new
|
6
6
|
|
7
7
|
REGISTRY = {}
|
8
8
|
|
9
|
-
# Lookup
|
9
|
+
# Lookup integration for name
|
10
10
|
#
|
11
11
|
# @param [String] name
|
12
12
|
#
|
13
|
-
# @return [
|
13
|
+
# @return [Integration]
|
14
14
|
# if found
|
15
15
|
#
|
16
16
|
# @api private
|
@@ -19,7 +19,7 @@ module Mutant
|
|
19
19
|
REGISTRY.fetch(name)
|
20
20
|
end
|
21
21
|
|
22
|
-
# Register
|
22
|
+
# Register integration
|
23
23
|
#
|
24
24
|
# @param [String] name
|
25
25
|
#
|
@@ -29,10 +29,12 @@ module Mutant
|
|
29
29
|
#
|
30
30
|
def self.register(name)
|
31
31
|
REGISTRY[name] = self
|
32
|
+
|
33
|
+
define_method(:name) { name }
|
32
34
|
end
|
33
35
|
private_class_method :register
|
34
36
|
|
35
|
-
# Perform
|
37
|
+
# Perform integration setup
|
36
38
|
#
|
37
39
|
# @return [self]
|
38
40
|
#
|
@@ -42,7 +44,7 @@ module Mutant
|
|
42
44
|
self
|
43
45
|
end
|
44
46
|
|
45
|
-
# Perform
|
47
|
+
# Perform integration teardown
|
46
48
|
#
|
47
49
|
# @return [self]
|
48
50
|
#
|
@@ -52,7 +54,7 @@ module Mutant
|
|
52
54
|
self
|
53
55
|
end
|
54
56
|
|
55
|
-
# Return all available tests by
|
57
|
+
# Return all available tests by integration
|
56
58
|
#
|
57
59
|
# @return [Enumerable<Test>]
|
58
60
|
#
|
@@ -60,28 +62,7 @@ module Mutant
|
|
60
62
|
#
|
61
63
|
abstract_method :all_tests
|
62
64
|
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# TODO: This logic is now centralized but still fucked.
|
66
|
-
#
|
67
|
-
# @param [Mutation] mutation
|
68
|
-
#
|
69
|
-
# @return [Enumerable<Test>]
|
70
|
-
#
|
71
|
-
# @api private
|
72
|
-
#
|
73
|
-
def tests(subject)
|
74
|
-
subject.match_prefixes.map do |match_expression|
|
75
|
-
tests = all_tests.select do |test|
|
76
|
-
test.subject_identification.start_with?(match_expression)
|
77
|
-
end
|
78
|
-
return tests if tests.any?
|
79
|
-
end
|
80
|
-
|
81
|
-
EMPTY_ARRAY
|
82
|
-
end
|
83
|
-
|
84
|
-
# Null strategy that never kills a mutation
|
65
|
+
# Null integration that never kills a mutation
|
85
66
|
class Null < self
|
86
67
|
|
87
68
|
register('null')
|
@@ -98,5 +79,5 @@ module Mutant
|
|
98
79
|
|
99
80
|
end # Null
|
100
81
|
|
101
|
-
end #
|
82
|
+
end # Integration
|
102
83
|
end # Mutant
|
data/lib/mutant/isolation.rb
CHANGED
data/lib/mutant/matcher.rb
CHANGED
@@ -3,29 +3,9 @@ module Mutant
|
|
3
3
|
class Matcher
|
4
4
|
include Adamantium::Flat, Enumerable, AbstractType
|
5
5
|
|
6
|
-
# Enumerate subjects
|
7
|
-
#
|
8
|
-
# @param [Object] input
|
9
|
-
#
|
10
|
-
# @return [self]
|
11
|
-
# if block given
|
12
|
-
#
|
13
|
-
# @return [Enumerable<Subject>]
|
14
|
-
# otherwise
|
15
|
-
#
|
16
|
-
# @api private
|
17
|
-
#
|
18
|
-
def self.each(cache, input, &block)
|
19
|
-
return to_enum(__method__, cache, input) unless block_given?
|
20
|
-
|
21
|
-
build(cache, input).each(&block)
|
22
|
-
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
6
|
# Default matcher build implementation
|
27
7
|
#
|
28
|
-
# @param [
|
8
|
+
# @param [Env] env
|
29
9
|
# @param [Object] input
|
30
10
|
#
|
31
11
|
# @return [undefined]
|