mutant 0.10.24 → 0.10.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.rb +15 -11
- data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
- data/lib/mutant/ast/regexp.rb +17 -0
- data/lib/mutant/ast/regexp/transformer.rb +2 -2
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +4 -2
- data/lib/mutant/bootstrap.rb +1 -1
- data/lib/mutant/cli/command.rb +4 -0
- data/lib/mutant/cli/command/environment/subject.rb +0 -4
- data/lib/mutant/cli/command/environment/test.rb +36 -0
- data/lib/mutant/cli/command/root.rb +1 -1
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/context.rb +1 -1
- data/lib/mutant/env.rb +2 -2
- data/lib/mutant/expression/method.rb +4 -4
- data/lib/mutant/expression/methods.rb +5 -4
- data/lib/mutant/integration.rb +8 -2
- data/lib/mutant/isolation/exception.rb +22 -0
- data/lib/mutant/isolation/fork.rb +9 -12
- data/lib/mutant/loader.rb +1 -1
- data/lib/mutant/matcher.rb +1 -1
- data/lib/mutant/matcher/config.rb +6 -3
- data/lib/mutant/matcher/method.rb +12 -10
- data/lib/mutant/matcher/method/instance.rb +6 -2
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/matcher/methods.rb +2 -4
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/meta/example/dsl.rb +0 -1
- data/lib/mutant/meta/example/verification.rb +1 -1
- data/lib/mutant/mutation.rb +1 -1
- data/lib/mutant/mutator.rb +8 -1
- data/lib/mutant/mutator/node.rb +0 -5
- data/lib/mutant/mutator/node/argument.rb +2 -2
- data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
- data/lib/mutant/mutator/node/index.rb +1 -0
- data/lib/mutant/mutator/node/literal/float.rb +1 -3
- data/lib/mutant/mutator/node/literal/integer.rb +3 -6
- data/lib/mutant/mutator/node/literal/range.rb +1 -1
- data/lib/mutant/mutator/node/literal/regex.rb +3 -17
- data/lib/mutant/mutator/node/module.rb +19 -0
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
- data/lib/mutant/mutator/node/regexp.rb +0 -11
- data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
- data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
- data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
- data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
- data/lib/mutant/mutator/node/regopt.rb +1 -1
- data/lib/mutant/mutator/node/sclass.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +73 -6
- data/lib/mutant/parallel.rb +2 -2
- data/lib/mutant/parallel/driver.rb +1 -1
- data/lib/mutant/parallel/worker.rb +1 -1
- data/lib/mutant/parser.rb +1 -1
- data/lib/mutant/pipe.rb +1 -1
- data/lib/mutant/procto.rb +23 -0
- data/lib/mutant/reporter/cli/printer.rb +10 -4
- data/lib/mutant/reporter/cli/printer/env.rb +3 -3
- data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +3 -1
- data/lib/mutant/selector.rb +1 -1
- data/lib/mutant/subject.rb +1 -1
- data/lib/mutant/subject/method/instance.rb +5 -42
- data/lib/mutant/transform.rb +25 -0
- data/lib/mutant/variable.rb +322 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +1 -1
- metadata +13 -160
- data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 287c627187afa2b42509eccfbb1f6c9d6a0122b6fc27bc518746ecc4db406da5
|
4
|
+
data.tar.gz: a7310138beb4d7b415f627d82cfcb7cd6149f9d9f06272c1b31e15bfa0ec7375
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 607876e535726ad8c39b1ba7d2b9cd18ca93e6579ee62142a38b31c9f4cd856d49f828398b7bee7e963ae8029b6b3f41772d2e3df6e527dd71bf303009f80f15
|
7
|
+
data.tar.gz: 7957de07e7554b3921be9311d4ea2fa67a4068c62794466853972157beeb504a20de97856001cf17decc9730b99df509c45a539db81769c42c0ecb31abd9d610
|
data/lib/mutant.rb
CHANGED
@@ -1,16 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'abstract_type'
|
4
|
-
require 'adamantium'
|
5
|
-
require 'anima'
|
6
|
-
require 'concord'
|
7
3
|
require 'diff/lcs'
|
8
4
|
require 'diff/lcs/hunk'
|
9
5
|
require 'digest/sha1'
|
10
|
-
require 'equalizer'
|
11
6
|
require 'etc'
|
12
|
-
require 'ice_nine'
|
13
|
-
require 'mprelude'
|
14
7
|
require 'json'
|
15
8
|
require 'open3'
|
16
9
|
require 'optparse'
|
@@ -22,7 +15,6 @@ require 'set'
|
|
22
15
|
require 'singleton'
|
23
16
|
require 'stringio'
|
24
17
|
require 'unparser'
|
25
|
-
require 'variable'
|
26
18
|
require 'yaml'
|
27
19
|
|
28
20
|
# This setting is done to make errors within the parallel
|
@@ -33,7 +25,12 @@ Thread.abort_on_exception = true
|
|
33
25
|
#
|
34
26
|
# @api private
|
35
27
|
module Mutant
|
36
|
-
|
28
|
+
AbstractType = Unparser::AbstractType
|
29
|
+
Adamantium = Unparser::Adamantium
|
30
|
+
Anima = Unparser::Anima
|
31
|
+
Concord = Unparser::Concord
|
32
|
+
Either = Unparser::Either
|
33
|
+
Equalizer = Unparser::Equalizer
|
37
34
|
|
38
35
|
EMPTY_STRING = ''
|
39
36
|
EMPTY_ARRAY = [].freeze
|
@@ -41,7 +38,9 @@ module Mutant
|
|
41
38
|
SCOPE_OPERATOR = '::'
|
42
39
|
end # Mutant
|
43
40
|
|
41
|
+
require 'mutant/procto'
|
44
42
|
require 'mutant/transform'
|
43
|
+
require 'mutant/variable'
|
45
44
|
require 'mutant/bootstrap'
|
46
45
|
require 'mutant/version'
|
47
46
|
require 'mutant/env'
|
@@ -72,8 +71,9 @@ require 'mutant/ast/meta/optarg'
|
|
72
71
|
require 'mutant/ast/meta/resbody'
|
73
72
|
require 'mutant/parser'
|
74
73
|
require 'mutant/isolation'
|
75
|
-
require 'mutant/isolation/
|
74
|
+
require 'mutant/isolation/exception'
|
76
75
|
require 'mutant/isolation/fork'
|
76
|
+
require 'mutant/isolation/none'
|
77
77
|
require 'mutant/parallel'
|
78
78
|
require 'mutant/parallel/driver'
|
79
79
|
require 'mutant/parallel/source'
|
@@ -88,11 +88,13 @@ require 'mutant/mutator/node'
|
|
88
88
|
require 'mutant/mutator/node/generic'
|
89
89
|
require 'mutant/mutator/node/regexp'
|
90
90
|
require 'mutant/mutator/node/regexp/alternation_meta'
|
91
|
+
require 'mutant/mutator/node/regexp/beginning_of_line_anchor'
|
91
92
|
require 'mutant/mutator/node/regexp/capture_group'
|
93
|
+
require 'mutant/mutator/node/regexp/named_group'
|
92
94
|
require 'mutant/mutator/node/regexp/character_type'
|
93
95
|
require 'mutant/mutator/node/regexp/end_of_line_anchor'
|
94
96
|
require 'mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor'
|
95
|
-
require 'mutant/mutator/node/regexp/
|
97
|
+
require 'mutant/mutator/node/regexp/zero_or_more'
|
96
98
|
require 'mutant/mutator/node/literal'
|
97
99
|
require 'mutant/mutator/node/literal/boolean'
|
98
100
|
require 'mutant/mutator/node/literal/range'
|
@@ -136,6 +138,7 @@ require 'mutant/mutator/node/define'
|
|
136
138
|
require 'mutant/mutator/node/mlhs'
|
137
139
|
require 'mutant/mutator/node/nthref'
|
138
140
|
require 'mutant/mutator/node/masgn'
|
141
|
+
require 'mutant/mutator/node/module'
|
139
142
|
require 'mutant/mutator/node/return'
|
140
143
|
require 'mutant/mutator/node/block'
|
141
144
|
require 'mutant/mutator/node/block_pass'
|
@@ -192,6 +195,7 @@ require 'mutant/cli/command/environment'
|
|
192
195
|
require 'mutant/cli/command/environment/run'
|
193
196
|
require 'mutant/cli/command/environment/show'
|
194
197
|
require 'mutant/cli/command/environment/subject'
|
198
|
+
require 'mutant/cli/command/environment/test'
|
195
199
|
require 'mutant/cli/command/root'
|
196
200
|
require 'mutant/runner'
|
197
201
|
require 'mutant/runner/sink'
|
@@ -10,7 +10,7 @@ module Mutant
|
|
10
10
|
# Descending into 'begin' nodes is supported because these are generated for
|
11
11
|
# the one-line syntax class << self; def foo; end
|
12
12
|
class FindMetaclassContaining
|
13
|
-
include NodePredicates, Concord.new(:root, :target), Procto
|
13
|
+
include NodePredicates, Concord.new(:root, :target), Procto
|
14
14
|
|
15
15
|
SCLASS_BODY_INDEX = 1
|
16
16
|
|
data/lib/mutant/ast/regexp.rb
CHANGED
@@ -32,6 +32,23 @@ module Mutant
|
|
32
32
|
def self.to_expression(node)
|
33
33
|
Transformer.lookup(node.type).to_expression(node)
|
34
34
|
end
|
35
|
+
|
36
|
+
# Convert's a `parser` `regexp` node into more fine-grained AST nodes.
|
37
|
+
#
|
38
|
+
# @param node [Parser::AST::Node]
|
39
|
+
#
|
40
|
+
# @return [Parser::AST::Node]
|
41
|
+
def self.expand_regexp_ast(node)
|
42
|
+
*body, _opts = node.children
|
43
|
+
|
44
|
+
# NOTE: We only mutate parts of regexp body if the body is composed of
|
45
|
+
# only strings. Regular expressions with interpolation are skipped
|
46
|
+
return unless body.all? { |child| child.type.equal?(:str) }
|
47
|
+
|
48
|
+
body_expression = parse(body.map(&:children).join)
|
49
|
+
|
50
|
+
to_ast(body_expression)
|
51
|
+
end
|
35
52
|
end # Regexp
|
36
53
|
end # AST
|
37
54
|
end # Mutant
|
@@ -49,7 +49,7 @@ module Mutant
|
|
49
49
|
class ExpressionToAST
|
50
50
|
PREFIX = :regexp
|
51
51
|
|
52
|
-
include Concord.new(:expression), Procto
|
52
|
+
include Concord.new(:expression), Procto, AST::Sexp, AbstractType, Adamantium
|
53
53
|
|
54
54
|
private
|
55
55
|
|
@@ -74,7 +74,7 @@ module Mutant
|
|
74
74
|
|
75
75
|
# Abstract node transformer
|
76
76
|
class ASTToExpression
|
77
|
-
include Concord.new(:node), Procto
|
77
|
+
include Concord.new(:node), Procto, AbstractType, Adamantium
|
78
78
|
|
79
79
|
# Call generic transform method and freeze result
|
80
80
|
#
|
@@ -30,7 +30,7 @@ module Mutant
|
|
30
30
|
|
31
31
|
Quantifier = Class.new.include(Concord::Public.new(:type, :suffix, :mode))
|
32
32
|
|
33
|
-
QUANTIFIER_MAP =
|
33
|
+
QUANTIFIER_MAP = {
|
34
34
|
regexp_greedy_zero_or_more: [:zero_or_more, '*', :greedy],
|
35
35
|
regexp_greedy_one_or_more: [:one_or_more, '+', :greedy],
|
36
36
|
regexp_greedy_zero_or_one: [:zero_or_one, '?', :greedy],
|
@@ -42,7 +42,9 @@ module Mutant
|
|
42
42
|
regexp_greedy_interval: [:interval, '', :greedy],
|
43
43
|
regexp_reluctant_interval: [:interval, '?', :reluctant],
|
44
44
|
regexp_possessive_interval: [:interval, '+', :possessive]
|
45
|
-
}.transform_values { |arguments| Quantifier.new(*arguments) }
|
45
|
+
}.transform_values { |arguments| Quantifier.new(*arguments) }
|
46
|
+
.to_h
|
47
|
+
.freeze
|
46
48
|
|
47
49
|
private
|
48
50
|
|
data/lib/mutant/bootstrap.rb
CHANGED
@@ -8,7 +8,7 @@ module Mutant
|
|
8
8
|
#
|
9
9
|
# env = config interpreted against the world
|
10
10
|
module Bootstrap
|
11
|
-
include Adamantium
|
11
|
+
include Adamantium, Anima.new(:config, :parser, :world)
|
12
12
|
|
13
13
|
SEMANTICS_MESSAGE_FORMAT =
|
14
14
|
"%<message>s. Fix your lib to follow normal ruby semantics!\n" \
|
data/lib/mutant/cli/command.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module CLI
|
5
|
+
class Command
|
6
|
+
class Environment
|
7
|
+
class Test < self
|
8
|
+
NAME = 'test'
|
9
|
+
SHORT_DESCRIPTION = 'test subcommands'
|
10
|
+
|
11
|
+
class List < self
|
12
|
+
NAME = 'list'
|
13
|
+
SHORT_DESCRIPTION = 'List tests detected in the environment'
|
14
|
+
SUBCOMMANDS = EMPTY_ARRAY
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def action
|
19
|
+
bootstrap.fmap(&method(:list_tests))
|
20
|
+
end
|
21
|
+
|
22
|
+
def list_tests(env)
|
23
|
+
tests = env.integration.all_tests
|
24
|
+
print('Tests in environment: %d' % tests.length)
|
25
|
+
tests.each do |test|
|
26
|
+
print(test.identification)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
SUBCOMMANDS = [List].freeze
|
32
|
+
end # Test
|
33
|
+
end # Environment
|
34
|
+
end # Command
|
35
|
+
end # CLI
|
36
|
+
end # Mutant
|
data/lib/mutant/config.rb
CHANGED
@@ -6,7 +6,7 @@ module Mutant
|
|
6
6
|
# Does not reference any "external" volatile state. The configuration applied
|
7
7
|
# to current environment is being represented by the Mutant::Env object.
|
8
8
|
class Config
|
9
|
-
include Adamantium
|
9
|
+
include Adamantium, Anima.new(
|
10
10
|
:coverage_criteria,
|
11
11
|
:expression_parser,
|
12
12
|
:fail_fast,
|
data/lib/mutant/context.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Mutant
|
4
4
|
# An abstract context where mutations can be applied to.
|
5
5
|
class Context
|
6
|
-
include Adamantium
|
6
|
+
include Adamantium, Concord::Public.new(:scope, :source_path)
|
7
7
|
extend AST::Sexp
|
8
8
|
|
9
9
|
NAMESPACE_DELIMITER = '::'
|
data/lib/mutant/env.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Mutant
|
4
4
|
# Abstract base class for mutant environments
|
5
5
|
class Env
|
6
|
-
include Adamantium
|
6
|
+
include Adamantium, Anima.new(
|
7
7
|
:config,
|
8
8
|
:integration,
|
9
9
|
:matchable_scopes,
|
@@ -31,7 +31,7 @@ module Mutant
|
|
31
31
|
config: config,
|
32
32
|
integration: Integration::Null.new(
|
33
33
|
expression_parser: config.expression_parser,
|
34
|
-
|
34
|
+
world: world
|
35
35
|
),
|
36
36
|
matchable_scopes: EMPTY_ARRAY,
|
37
37
|
mutations: EMPTY_ARRAY,
|
@@ -15,10 +15,10 @@ module Mutant
|
|
15
15
|
|
16
16
|
private(*anima.attribute_names)
|
17
17
|
|
18
|
-
MATCHERS =
|
19
|
-
'.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
|
20
|
-
'#' => [Matcher::Methods::Instance]
|
21
|
-
|
18
|
+
MATCHERS = {
|
19
|
+
'.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass].freeze,
|
20
|
+
'#' => [Matcher::Methods::Instance].freeze
|
21
|
+
}.freeze
|
22
22
|
|
23
23
|
METHOD_NAME_PATTERN = /(?<method_name>.+)/.freeze
|
24
24
|
|
@@ -12,10 +12,11 @@ module Mutant
|
|
12
12
|
|
13
13
|
private(*anima.attribute_names)
|
14
14
|
|
15
|
-
MATCHERS =
|
16
|
-
'.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
|
17
|
-
'#' => [Matcher::Methods::Instance]
|
18
|
-
|
15
|
+
MATCHERS = {
|
16
|
+
'.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass].freeze,
|
17
|
+
'#' => [Matcher::Methods::Instance].freeze
|
18
|
+
}.freeze
|
19
|
+
|
19
20
|
private_constant(*constants(false))
|
20
21
|
|
21
22
|
REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}\z/.freeze
|
data/lib/mutant/integration.rb
CHANGED
@@ -4,7 +4,7 @@ module Mutant
|
|
4
4
|
|
5
5
|
# Abstract base class mutant test framework integrations
|
6
6
|
class Integration
|
7
|
-
include AbstractType, Adamantium
|
7
|
+
include AbstractType, Adamantium, Anima.new(:expression_parser, :world)
|
8
8
|
|
9
9
|
LOAD_MESSAGE = <<~'MESSAGE'
|
10
10
|
Unable to load integration mutant-%<integration_name>s:
|
@@ -39,7 +39,7 @@ module Mutant
|
|
39
39
|
attempt_require(env).bind { attempt_const_get(env) }.fmap do |klass|
|
40
40
|
klass.new(
|
41
41
|
expression_parser: env.config.expression_parser,
|
42
|
-
|
42
|
+
world: env.world
|
43
43
|
).setup
|
44
44
|
end
|
45
45
|
end
|
@@ -92,5 +92,11 @@ module Mutant
|
|
92
92
|
#
|
93
93
|
# @return [Enumerable<Test>]
|
94
94
|
abstract_method :all_tests
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def timer
|
99
|
+
world.timer
|
100
|
+
end
|
95
101
|
end # Integration
|
96
102
|
end # Mutant
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
# Module providing isolation
|
5
|
+
class Isolation
|
6
|
+
# Generic serializable exception data.
|
7
|
+
#
|
8
|
+
# This is required as our honored guests the Rails* ecosystem
|
9
|
+
# makes Marshal.dump on exceptions impossible.
|
10
|
+
#
|
11
|
+
# @see https://twitter.com/_m_b_j_/status/1356433184850907137
|
12
|
+
#
|
13
|
+
# for the full story and eventual reactions.
|
14
|
+
class Exception
|
15
|
+
include Anima.new(
|
16
|
+
:backtrace,
|
17
|
+
:message,
|
18
|
+
:original_class
|
19
|
+
)
|
20
|
+
end # Exception
|
21
|
+
end # Isolation
|
22
|
+
end # Mutant
|
@@ -28,7 +28,7 @@ module Mutant
|
|
28
28
|
# * Child process freezing after closing the pipes needs to be
|
29
29
|
# detected by parent process.
|
30
30
|
class Fork < self
|
31
|
-
include(Adamantium
|
31
|
+
include(Adamantium, Concord.new(:world))
|
32
32
|
|
33
33
|
READ_SIZE = 4096
|
34
34
|
|
@@ -36,7 +36,7 @@ module Mutant
|
|
36
36
|
|
37
37
|
# Pipe abstraction
|
38
38
|
class Pipe
|
39
|
-
include Adamantium
|
39
|
+
include Adamantium, Anima.new(:reader, :writer)
|
40
40
|
|
41
41
|
# Run block with pipe in binmode
|
42
42
|
#
|
@@ -66,10 +66,7 @@ module Mutant
|
|
66
66
|
|
67
67
|
# rubocop:disable Metrics/ClassLength
|
68
68
|
class Parent
|
69
|
-
include(
|
70
|
-
Anima.new(*ATTRIBUTES),
|
71
|
-
Procto.call
|
72
|
-
)
|
69
|
+
include(Anima.new(*ATTRIBUTES), Procto)
|
73
70
|
|
74
71
|
# Prevent mutation from `process.fork` to `fork` to call Kernel#fork
|
75
72
|
undef_method :fork
|
@@ -136,7 +133,11 @@ module Mutant
|
|
136
133
|
def load_result(result_fragments)
|
137
134
|
@value = world.marshal.load(result_fragments.join)
|
138
135
|
rescue ArgumentError => exception
|
139
|
-
@exception =
|
136
|
+
@exception = Exception.new(
|
137
|
+
backtrace: exception.backtrace,
|
138
|
+
message: exception.message,
|
139
|
+
original_class: exception.class
|
140
|
+
)
|
140
141
|
end
|
141
142
|
|
142
143
|
# rubocop:disable Metrics/MethodLength
|
@@ -212,11 +213,7 @@ module Mutant
|
|
212
213
|
# rubocop:enable Metrics/ClassLength
|
213
214
|
|
214
215
|
class Child
|
215
|
-
include(
|
216
|
-
Adamantium::Flat,
|
217
|
-
Anima.new(*ATTRIBUTES),
|
218
|
-
Procto.call
|
219
|
-
)
|
216
|
+
include(Adamantium, Anima.new(*ATTRIBUTES), Procto)
|
220
217
|
|
221
218
|
# Handle child process
|
222
219
|
#
|