mutant 0.8.10 → 0.8.11
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/.rubocop.yml +1 -4
- data/Changelog.md +8 -0
- data/README.md +112 -43
- data/Rakefile +2 -16
- data/circle.yml +1 -1
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +3 -4
- data/config/rubocop.yml +53 -16
- data/lib/mutant.rb +27 -6
- data/lib/mutant/ast/meta/const.rb +2 -0
- data/lib/mutant/ast/meta/optarg.rb +2 -0
- data/lib/mutant/ast/meta/resbody.rb +2 -0
- data/lib/mutant/ast/meta/restarg.rb +2 -0
- data/lib/mutant/ast/meta/send.rb +4 -0
- data/lib/mutant/ast/meta/symbol.rb +2 -0
- data/lib/mutant/ast/named_children.rb +14 -4
- data/lib/mutant/ast/nodes.rb +3 -1
- data/lib/mutant/ast/regexp.rb +53 -0
- data/lib/mutant/ast/regexp/transformer.rb +185 -0
- data/lib/mutant/ast/regexp/transformer/alternative.rb +39 -0
- data/lib/mutant/ast/regexp/transformer/character_set.rb +46 -0
- data/lib/mutant/ast/regexp/transformer/direct.rb +99 -0
- data/lib/mutant/ast/regexp/transformer/options_group.rb +66 -0
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +112 -0
- data/lib/mutant/ast/regexp/transformer/recursive.rb +50 -0
- data/lib/mutant/ast/regexp/transformer/root.rb +29 -0
- data/lib/mutant/ast/regexp/transformer/text.rb +55 -0
- data/lib/mutant/ast/types.rb +92 -5
- data/lib/mutant/cli.rb +2 -14
- data/lib/mutant/color.rb +1 -1
- data/lib/mutant/config.rb +1 -3
- data/lib/mutant/expression/methods.rb +1 -1
- data/lib/mutant/expression/namespace.rb +2 -2
- data/lib/mutant/expression/parser.rb +1 -1
- data/lib/mutant/integration.rb +10 -28
- data/lib/mutant/isolation.rb +9 -60
- data/lib/mutant/isolation/fork.rb +72 -0
- data/lib/mutant/isolation/none.rb +25 -0
- data/lib/mutant/matcher/config.rb +2 -0
- data/lib/mutant/matcher/method/singleton.rb +5 -4
- data/lib/mutant/meta.rb +11 -4
- data/lib/mutant/meta/example.rb +2 -116
- data/lib/mutant/meta/example/dsl.rb +22 -19
- data/lib/mutant/meta/example/verification.rb +86 -0
- data/lib/mutant/mutator.rb +22 -49
- data/lib/mutant/mutator/node.rb +15 -19
- data/lib/mutant/mutator/node/and_asgn.rb +1 -1
- data/lib/mutant/mutator/node/argument.rb +10 -5
- data/lib/mutant/mutator/node/arguments.rb +5 -9
- data/lib/mutant/mutator/node/begin.rb +4 -17
- data/lib/mutant/mutator/node/block.rb +1 -1
- data/lib/mutant/mutator/node/break.rb +1 -1
- data/lib/mutant/mutator/node/class.rb +21 -0
- data/lib/mutant/mutator/node/conditional_loop.rb +1 -1
- data/lib/mutant/mutator/node/define.rb +1 -1
- data/lib/mutant/mutator/node/defined.rb +1 -1
- data/lib/mutant/mutator/node/dstr.rb +1 -1
- data/lib/mutant/mutator/node/dsym.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +3 -3
- data/lib/mutant/mutator/node/kwbegin.rb +1 -1
- data/lib/mutant/mutator/node/literal.rb +9 -0
- data/lib/mutant/mutator/node/literal/boolean.rb +1 -1
- data/lib/mutant/mutator/node/literal/fixnum.rb +2 -2
- data/lib/mutant/mutator/node/literal/float.rb +4 -6
- data/lib/mutant/mutator/node/literal/hash.rb +1 -1
- data/lib/mutant/mutator/node/literal/nil.rb +1 -1
- data/lib/mutant/mutator/node/literal/range.rb +2 -19
- data/lib/mutant/mutator/node/literal/regex.rb +43 -3
- data/lib/mutant/mutator/node/literal/string.rb +1 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +2 -4
- data/lib/mutant/mutator/node/masgn.rb +1 -1
- data/lib/mutant/mutator/node/match_current_line.rb +1 -1
- data/lib/mutant/mutator/node/mlhs.rb +2 -3
- data/lib/mutant/mutator/node/named_value/access.rb +2 -2
- data/lib/mutant/mutator/node/named_value/constant_assignment.rb +4 -3
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +4 -4
- data/lib/mutant/mutator/node/next.rb +1 -1
- data/lib/mutant/mutator/node/nthref.rb +1 -1
- data/lib/mutant/mutator/node/or_asgn.rb +1 -1
- data/lib/mutant/mutator/node/regexp.rb +44 -0
- data/lib/mutant/mutator/node/regopt.rb +31 -0
- data/lib/mutant/mutator/node/resbody.rb +1 -1
- data/lib/mutant/mutator/node/rescue.rb +1 -3
- data/lib/mutant/mutator/node/return.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +43 -3
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +4 -1
- data/lib/mutant/mutator/node/send/conditional.rb +23 -0
- data/lib/mutant/mutator/node/send/index.rb +1 -1
- data/lib/mutant/mutator/node/splat.rb +1 -1
- data/lib/mutant/mutator/node/when.rb +1 -1
- data/lib/mutant/mutator/node/yield.rb +1 -1
- data/lib/mutant/mutator/util.rb +0 -30
- data/lib/mutant/mutator/util/array.rb +4 -16
- data/lib/mutant/parallel.rb +1 -1
- data/lib/mutant/parallel/worker.rb +1 -1
- data/lib/mutant/registry.rb +44 -0
- data/lib/mutant/reporter/cli/format.rb +2 -0
- data/lib/mutant/reporter/cli/printer.rb +2 -2
- data/lib/mutant/reporter/cli/printer/config.rb +0 -1
- data/lib/mutant/reporter/cli/printer/env_progress.rb +1 -11
- data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +1 -1
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +2 -0
- data/lib/mutant/reporter/cli/tput.rb +1 -1
- data/lib/mutant/reporter/sequence.rb +3 -0
- data/lib/mutant/require_highjack.rb +6 -2
- data/lib/mutant/result.rb +1 -1
- data/lib/mutant/subject.rb +5 -5
- data/lib/mutant/subject/method/instance.rb +1 -2
- data/lib/mutant/util.rb +18 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/zombifier.rb +5 -3
- data/meta/and.rb +1 -1
- data/meta/and_asgn.rb +1 -1
- data/meta/array.rb +2 -2
- data/meta/begin.rb +2 -2
- data/meta/block.rb +7 -7
- data/meta/block_pass.rb +1 -1
- data/meta/blockarg.rb +1 -1
- data/meta/break.rb +1 -1
- data/meta/case.rb +2 -2
- data/meta/casgn.rb +11 -2
- data/meta/cbase.rb +1 -1
- data/meta/class.rb +10 -0
- data/meta/const.rb +9 -1
- data/meta/csend.rb +8 -0
- data/meta/cvar.rb +1 -1
- data/meta/cvasgn.rb +1 -1
- data/meta/date.rb +4 -4
- data/meta/def.rb +14 -14
- data/meta/defined.rb +1 -1
- data/meta/dstr.rb +1 -1
- data/meta/dsym.rb +1 -1
- data/meta/ensure.rb +1 -1
- data/meta/false.rb +1 -1
- data/meta/float.rb +3 -3
- data/meta/gvar.rb +1 -1
- data/meta/gvasgn.rb +1 -1
- data/meta/hash.rb +1 -1
- data/meta/if.rb +17 -3
- data/meta/int.rb +1 -1
- data/meta/ivar.rb +1 -1
- data/meta/ivasgn.rb +14 -1
- data/meta/kwarg.rb +8 -0
- data/meta/kwbegin.rb +1 -1
- data/meta/kwoptarg.rb +11 -0
- data/meta/lvar.rb +1 -1
- data/meta/lvasgn.rb +1 -1
- data/meta/masgn.rb +1 -1
- data/meta/match_current_line.rb +2 -2
- data/meta/next.rb +1 -1
- data/meta/nil.rb +1 -1
- data/meta/nthref.rb +5 -5
- data/meta/op_assgn.rb +1 -1
- data/meta/or.rb +1 -1
- data/meta/or_asgn.rb +5 -5
- data/meta/range.rb +2 -8
- data/meta/redo.rb +1 -1
- data/meta/regexp.rb +106 -0
- data/meta/regexp/regexp_bol_anchor.rb +13 -0
- data/meta/regexp/regexp_bos_anchor.rb +26 -0
- data/meta/regexp/regexp_root_expression.rb +13 -0
- data/meta/regopt.rb +8 -0
- data/meta/rescue.rb +49 -4
- data/meta/restarg.rb +6 -9
- data/meta/return.rb +2 -2
- data/meta/self.rb +1 -1
- data/meta/send.rb +228 -55
- data/meta/str.rb +1 -1
- data/meta/super.rb +3 -3
- data/meta/{symbol.rb → sym.rb} +1 -1
- data/meta/true.rb +1 -1
- data/meta/until.rb +1 -1
- data/meta/while.rb +2 -2
- data/meta/yield.rb +1 -1
- data/mutant-rspec.gemspec +2 -2
- data/mutant.gemspec +6 -5
- data/spec/integration/mutant/isolation/fork_spec.rb +8 -0
- data/spec/integration/mutant/rspec_spec.rb +1 -1
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -2
- data/spec/integrations.yml +93 -24
- data/spec/spec_helper.rb +12 -7
- data/spec/support/compress_helper.rb +1 -1
- data/spec/support/corpus.rb +115 -50
- data/spec/support/fake_actor.rb +5 -5
- data/spec/support/file_system.rb +1 -1
- data/spec/support/ice_nine_config.rb +3 -3
- data/spec/support/ruby_vm.rb +11 -12
- data/spec/support/shared_context.rb +22 -13
- data/spec/support/test_app.rb +1 -1
- data/spec/support/warning.rb +64 -0
- data/spec/support/warnings.yml +4 -0
- data/spec/support/xspec.rb +177 -0
- data/spec/unit/mutant/actor/env_spec.rb +2 -2
- data/spec/unit/mutant/actor/sender_spec.rb +1 -1
- data/spec/unit/mutant/ast/meta/send_spec.rb +1 -1
- data/spec/unit/mutant/ast/named_children_spec.rb +26 -0
- data/spec/unit/mutant/ast/regexp/parse_spec.rb +7 -0
- data/spec/unit/mutant/ast/regexp/supported_predicate_spec.rb +14 -0
- data/spec/unit/mutant/ast/regexp/transformer/lookup_table/table_spec.rb +19 -0
- data/spec/unit/mutant/ast/regexp/transformer/lookup_table_spec.rb +33 -0
- data/spec/unit/mutant/ast/regexp/transformer_spec.rb +19 -0
- data/spec/unit/mutant/ast/regexp_spec.rb +617 -0
- data/spec/unit/mutant/cli_spec.rb +7 -45
- data/spec/unit/mutant/context_spec.rb +4 -7
- data/spec/unit/mutant/env/{boostrap_spec.rb → bootstrap_spec.rb} +2 -2
- data/spec/unit/mutant/env_spec.rb +13 -16
- data/spec/unit/mutant/expression/namespace/{flat_spec.rb → exact_spec.rb} +0 -0
- data/spec/unit/mutant/integration/rspec_spec.rb +2 -2
- data/spec/unit/mutant/integration_spec.rb +14 -0
- data/spec/unit/mutant/isolation/fork_spec.rb +155 -0
- data/spec/unit/mutant/isolation/none_spec.rb +16 -0
- data/spec/unit/mutant/loader_spec.rb +1 -1
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +2 -4
- data/spec/unit/mutant/meta/example/dsl_spec.rb +106 -0
- data/spec/unit/mutant/meta/example/verification_spec.rb +120 -0
- data/spec/unit/mutant/meta/example_spec.rb +32 -0
- data/spec/unit/mutant/mutator/node_spec.rb +37 -4
- data/spec/unit/mutant/mutator_spec.rb +21 -0
- data/spec/unit/mutant/{runner → parallel}/driver_spec.rb +0 -0
- data/spec/unit/mutant/parallel/master_spec.rb +13 -13
- data/spec/unit/mutant/registry_spec.rb +47 -0
- data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +0 -4
- data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +0 -8
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +0 -2
- data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +0 -8
- data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +0 -34
- data/spec/unit/mutant/reporter/cli_spec.rb +0 -22
- data/spec/unit/mutant/repository/diff_spec.rb +6 -6
- data/spec/unit/mutant/require_highjack_spec.rb +38 -14
- data/spec/unit/mutant/result/env_spec.rb +1 -4
- data/spec/unit/mutant/runner_spec.rb +1 -1
- data/spec/unit/mutant/subject/method/instance_spec.rb +1 -1
- data/spec/unit/mutant/subject_spec.rb +3 -3
- data/spec/unit/mutant/util/one_spec.rb +20 -0
- data/spec/unit/mutant/zombifier_spec.rb +18 -18
- data/test_app/{Gemfile.rspec3.3 → Gemfile.rspec3.5} +2 -2
- metadata +94 -24
- data/TODO +0 -21
- data/lib/mutant/mutator/node/blockarg.rb +0 -13
- data/lib/mutant/mutator/node/restarg.rb +0 -13
- data/lib/mutant/mutator/registry.rb +0 -49
- data/meta/boolean.rb +0 -13
- data/meta/regex.rb +0 -19
- data/spec/unit/mutant/isolation_spec.rb +0 -104
- data/spec/unit/mutant/mutator/registry_spec.rb +0 -57
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Module providing isolation
|
3
|
+
class Isolation
|
4
|
+
Error = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
# Absolutly no isolation
|
7
|
+
#
|
8
|
+
# Only useful for debugging.
|
9
|
+
class None < self
|
10
|
+
|
11
|
+
# Call block in no isolation
|
12
|
+
#
|
13
|
+
# @return [Object]
|
14
|
+
#
|
15
|
+
# @raise [Error]
|
16
|
+
# if block terminates abnormal
|
17
|
+
def call
|
18
|
+
yield
|
19
|
+
rescue => exception
|
20
|
+
raise Error, exception
|
21
|
+
end
|
22
|
+
|
23
|
+
end # None
|
24
|
+
end # Isolation
|
25
|
+
end # Mutant
|
@@ -16,9 +16,10 @@ module Mutant
|
|
16
16
|
|
17
17
|
# Singleton method evaluator
|
18
18
|
class Evaluator < Evaluator
|
19
|
-
SUBJECT_CLASS
|
20
|
-
RECEIVER_INDEX
|
21
|
-
NAME_INDEX
|
19
|
+
SUBJECT_CLASS = Subject::Method::Singleton
|
20
|
+
RECEIVER_INDEX = 0
|
21
|
+
NAME_INDEX = 1
|
22
|
+
RECEIVER_WARNING = 'Can only match :defs on :self or :const got %p unable to match'.freeze
|
22
23
|
|
23
24
|
private
|
24
25
|
|
@@ -65,7 +66,7 @@ module Mutant
|
|
65
66
|
when :const
|
66
67
|
receiver_name?(receiver)
|
67
68
|
else
|
68
|
-
env.warn(
|
69
|
+
env.warn(RECEIVER_WARNING % receiver.type)
|
69
70
|
nil
|
70
71
|
end
|
71
72
|
end
|
data/lib/mutant/meta.rb
CHANGED
@@ -3,26 +3,33 @@ module Mutant
|
|
3
3
|
module Meta
|
4
4
|
require 'mutant/meta/example'
|
5
5
|
require 'mutant/meta/example/dsl'
|
6
|
+
require 'mutant/meta/example/verification'
|
6
7
|
|
7
8
|
# Mutation example
|
8
9
|
class Example
|
9
10
|
|
11
|
+
# rubocop:disable MutableConstant
|
10
12
|
ALL = []
|
11
13
|
|
12
14
|
# Add example
|
13
15
|
#
|
14
16
|
# @return [undefined]
|
15
|
-
def self.add(&block)
|
17
|
+
def self.add(type, &block)
|
16
18
|
file = caller.first.split(':in', 2).first
|
17
|
-
ALL << DSL.
|
19
|
+
ALL << DSL.call(file, type, block)
|
18
20
|
end
|
19
21
|
|
20
|
-
Pathname.glob(Pathname.new(
|
22
|
+
Pathname.glob(Pathname.new(__dir__).parent.parent.join('meta', '*.rb'))
|
21
23
|
.sort
|
22
24
|
.each(&method(:require))
|
25
|
+
|
23
26
|
ALL.freeze
|
24
27
|
|
25
|
-
|
28
|
+
# Remove mutation method only present for DSL executions from meta/**/*.rb
|
29
|
+
class << self
|
30
|
+
undef_method :add
|
31
|
+
end
|
26
32
|
|
33
|
+
end # Example
|
27
34
|
end # Meta
|
28
35
|
end # Mutant
|
data/lib/mutant/meta/example.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Mutant
|
2
2
|
module Meta
|
3
3
|
class Example
|
4
|
-
include Adamantium,
|
4
|
+
include Adamantium, Anima.new(:file, :node, :node_type, :expected)
|
5
5
|
|
6
6
|
# Verification instance for example
|
7
7
|
#
|
@@ -22,126 +22,12 @@ module Mutant
|
|
22
22
|
#
|
23
23
|
# @return [Enumerable<Mutant::Mutation>]
|
24
24
|
def generated
|
25
|
-
Mutator.
|
25
|
+
Mutator.mutate(node).map do |node|
|
26
26
|
Mutation::Evil.new(self, node)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
memoize :generated
|
30
30
|
|
31
|
-
# Example verification
|
32
|
-
class Verification
|
33
|
-
include Adamantium::Flat, Concord.new(:example, :mutations)
|
34
|
-
|
35
|
-
# Test if mutation was verified successfully
|
36
|
-
#
|
37
|
-
# @return [Boolean]
|
38
|
-
def success?
|
39
|
-
unparser.success? && missing.empty? && unexpected.empty? && no_diffs.empty?
|
40
|
-
end
|
41
|
-
|
42
|
-
# Error report
|
43
|
-
#
|
44
|
-
# @return [String]
|
45
|
-
def error_report
|
46
|
-
unless unparser.success?
|
47
|
-
return unparser.report
|
48
|
-
end
|
49
|
-
mutation_report
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
# Unexpected mutations
|
55
|
-
#
|
56
|
-
# @return [Array<Parser::AST::Node>]
|
57
|
-
def unexpected
|
58
|
-
mutations.map(&:node) - example.mutations
|
59
|
-
end
|
60
|
-
memoize :unexpected
|
61
|
-
|
62
|
-
# Mutations with no diff to original
|
63
|
-
#
|
64
|
-
# @return [Enumerable<Mutation>]
|
65
|
-
def no_diffs
|
66
|
-
mutations.select { |mutation| mutation.source.eql?(example.source) }
|
67
|
-
end
|
68
|
-
memoize :no_diffs
|
69
|
-
|
70
|
-
# Mutation report
|
71
|
-
#
|
72
|
-
# @return [String]
|
73
|
-
def mutation_report
|
74
|
-
original_node = example.node
|
75
|
-
[
|
76
|
-
"#{example.file}, Original-AST:",
|
77
|
-
original_node.inspect,
|
78
|
-
'Original-Source:',
|
79
|
-
example.source,
|
80
|
-
*missing_report,
|
81
|
-
*unexpected_report,
|
82
|
-
*no_diff_report
|
83
|
-
].join("\n======\n")
|
84
|
-
end
|
85
|
-
|
86
|
-
# Missing mutation report
|
87
|
-
#
|
88
|
-
# @return [Array, nil]
|
89
|
-
def missing_report
|
90
|
-
[
|
91
|
-
'Missing mutations:',
|
92
|
-
missing.map(&method(:format_mutation)).join("\n-----\n")
|
93
|
-
] if missing.any?
|
94
|
-
end
|
95
|
-
|
96
|
-
# No diff mutation report
|
97
|
-
#
|
98
|
-
# @return [Array, nil]
|
99
|
-
def no_diff_report
|
100
|
-
[
|
101
|
-
'No source diffs to original:',
|
102
|
-
no_diffs.map do |mutation|
|
103
|
-
"#{mutation.node.inspect}\n#{mutation.source}"
|
104
|
-
end
|
105
|
-
] if no_diffs.any?
|
106
|
-
end
|
107
|
-
|
108
|
-
# Unexpected mutation report
|
109
|
-
#
|
110
|
-
# @return [Array, nil]
|
111
|
-
def unexpected_report
|
112
|
-
[
|
113
|
-
'Unexpected mutations:',
|
114
|
-
unexpected.map(&method(:format_mutation)).join("\n-----\n")
|
115
|
-
] if unexpected.any?
|
116
|
-
end
|
117
|
-
|
118
|
-
# Format mutation
|
119
|
-
#
|
120
|
-
# @return [String]
|
121
|
-
def format_mutation(node)
|
122
|
-
[
|
123
|
-
node.inspect,
|
124
|
-
Unparser.unparse(node)
|
125
|
-
].join("\n")
|
126
|
-
end
|
127
|
-
|
128
|
-
# Missing mutations
|
129
|
-
#
|
130
|
-
# @return [Array<Parser::AST::Node>]
|
131
|
-
def missing
|
132
|
-
example.mutations - mutations.map(&:node)
|
133
|
-
end
|
134
|
-
memoize :missing
|
135
|
-
|
136
|
-
# Unparser verifier
|
137
|
-
#
|
138
|
-
# @return [Unparser::CLI::Source]
|
139
|
-
def unparser
|
140
|
-
Unparser::CLI::Source::Node.new(Unparser::Preprocessor.run(example.node))
|
141
|
-
end
|
142
|
-
memoize :unparser
|
143
|
-
|
144
|
-
end # Verification
|
145
31
|
end # Example
|
146
32
|
end # Meta
|
147
33
|
end # Mutant
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Mutant
|
2
2
|
module Meta
|
3
3
|
class Example
|
4
|
-
|
5
4
|
# Example DSL
|
6
5
|
class DSL
|
7
6
|
include AST::Sexp
|
@@ -9,18 +8,21 @@ module Mutant
|
|
9
8
|
# Run DSL on block
|
10
9
|
#
|
11
10
|
# @return [Example]
|
12
|
-
def self.
|
13
|
-
instance = new(file)
|
11
|
+
def self.call(file, type, block)
|
12
|
+
instance = new(file, type)
|
14
13
|
instance.instance_eval(&block)
|
15
14
|
instance.example
|
16
15
|
end
|
17
16
|
|
18
|
-
|
17
|
+
private_class_method :new
|
18
|
+
|
19
|
+
# Initialize object
|
19
20
|
#
|
20
21
|
# @return [undefined]
|
21
|
-
def initialize(file)
|
22
|
-
@file
|
23
|
-
@
|
22
|
+
def initialize(file, type)
|
23
|
+
@file = file
|
24
|
+
@type = type
|
25
|
+
@node = nil
|
24
26
|
@expected = []
|
25
27
|
end
|
26
28
|
|
@@ -31,8 +33,13 @@ module Mutant
|
|
31
33
|
# @raise [RuntimeError]
|
32
34
|
# in case example cannot be build
|
33
35
|
def example
|
34
|
-
fail 'source not defined' unless @
|
35
|
-
Example.new(
|
36
|
+
fail 'source not defined' unless @node
|
37
|
+
Example.new(
|
38
|
+
file: @file,
|
39
|
+
node: @node,
|
40
|
+
node_type: @type,
|
41
|
+
expected: @expected
|
42
|
+
)
|
36
43
|
end
|
37
44
|
|
38
45
|
private
|
@@ -41,27 +48,23 @@ module Mutant
|
|
41
48
|
#
|
42
49
|
# @param [String,Parser::AST::Node] input
|
43
50
|
#
|
44
|
-
# @return [
|
51
|
+
# @return [undefined]
|
45
52
|
def source(input)
|
46
|
-
fail 'source already defined' if @
|
47
|
-
@
|
48
|
-
|
49
|
-
self
|
53
|
+
fail 'source already defined' if @node
|
54
|
+
@node = node(input)
|
50
55
|
end
|
51
56
|
|
52
57
|
# Add expected mutation
|
53
58
|
#
|
54
59
|
# @param [String,Parser::AST::Node] input
|
55
60
|
#
|
56
|
-
# @return [
|
61
|
+
# @return [undefined]
|
57
62
|
def mutation(input)
|
58
63
|
node = node(input)
|
59
64
|
if @expected.include?(node)
|
60
|
-
fail "
|
65
|
+
fail "Mutation for input: #{input.inspect} is already expected"
|
61
66
|
end
|
62
67
|
@expected << node
|
63
|
-
|
64
|
-
self
|
65
68
|
end
|
66
69
|
|
67
70
|
# Add singleton mutations
|
@@ -87,7 +90,7 @@ module Mutant
|
|
87
90
|
when ::Parser::AST::Node
|
88
91
|
input
|
89
92
|
else
|
90
|
-
fail "Cannot coerce to node: #{
|
93
|
+
fail "Cannot coerce to node: #{input.inspect}"
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Meta
|
3
|
+
class Example
|
4
|
+
# Example verification
|
5
|
+
class Verification
|
6
|
+
include Adamantium::Flat, Concord.new(:example, :mutations)
|
7
|
+
|
8
|
+
# Test if mutation was verified successfully
|
9
|
+
#
|
10
|
+
# @return [Boolean]
|
11
|
+
def success?
|
12
|
+
missing.empty? && unexpected.empty? && no_diffs.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Error report
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
def error_report
|
19
|
+
fail 'no error report on successful validation' if success?
|
20
|
+
|
21
|
+
YAML.dump(
|
22
|
+
'file' => example.file,
|
23
|
+
'original_ast' => example.node.inspect,
|
24
|
+
'original_source' => example.source,
|
25
|
+
'missing' => format_mutations(missing),
|
26
|
+
'unexpected' => format_mutations(unexpected),
|
27
|
+
'no_diff' => no_diff_report
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Unexpected mutations
|
34
|
+
#
|
35
|
+
# @return [Array<Parser::AST::Node>]
|
36
|
+
def unexpected
|
37
|
+
mutations.map(&:node) - example.expected
|
38
|
+
end
|
39
|
+
memoize :unexpected
|
40
|
+
|
41
|
+
# Mutations with no diff to original
|
42
|
+
#
|
43
|
+
# @return [Enumerable<Mutation>]
|
44
|
+
def no_diffs
|
45
|
+
mutations.select { |mutation| mutation.source.eql?(example.source) }
|
46
|
+
end
|
47
|
+
memoize :no_diffs
|
48
|
+
|
49
|
+
# Mutation report
|
50
|
+
#
|
51
|
+
# @param [Array<Parser::AST::Node>] nodes
|
52
|
+
#
|
53
|
+
# @return [Array<Hash>]
|
54
|
+
def format_mutations(nodes)
|
55
|
+
nodes.map do |node|
|
56
|
+
{
|
57
|
+
'node' => node.inspect,
|
58
|
+
'source' => Unparser.unparse(node)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# No diff mutation report
|
64
|
+
#
|
65
|
+
# @return [Array, nil]
|
66
|
+
def no_diff_report
|
67
|
+
no_diffs.map do |mutation|
|
68
|
+
{
|
69
|
+
'node' => mutation.node.inspect,
|
70
|
+
'source' => mutation.source
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Missing mutations
|
76
|
+
#
|
77
|
+
# @return [Array<Parser::AST::Node>]
|
78
|
+
def missing
|
79
|
+
example.expected - mutations.map(&:node)
|
80
|
+
end
|
81
|
+
memoize :missing
|
82
|
+
|
83
|
+
end # Verification
|
84
|
+
end # Example
|
85
|
+
end # Meta
|
86
|
+
end # Mutant
|
data/lib/mutant/mutator.rb
CHANGED
@@ -1,21 +1,19 @@
|
|
1
1
|
module Mutant
|
2
2
|
# Generator for mutations
|
3
3
|
class Mutator
|
4
|
-
include Adamantium::Flat, AbstractType
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
REGISTRY = Registry.new
|
6
|
+
|
7
|
+
include Adamantium::Flat, Concord.new(:input, :parent), AbstractType, Procto.call(:output)
|
8
|
+
|
9
|
+
# Lookup and invoke dedicated AST mutator
|
10
10
|
#
|
11
|
-
# @param [
|
11
|
+
# @param node [Parser::AST::Node]
|
12
|
+
# @param parent [nil,Mutant::Mutator::Node]
|
12
13
|
#
|
13
|
-
# @return [
|
14
|
-
def self.
|
15
|
-
|
16
|
-
REGISTRY.lookup(input).new(input, parent, block)
|
17
|
-
|
18
|
-
self
|
14
|
+
# @return [Set<Parser::AST::Node>]
|
15
|
+
def self.mutate(node, parent = nil)
|
16
|
+
self::REGISTRY.lookup(node.type).call(node, parent)
|
19
17
|
end
|
20
18
|
|
21
19
|
# Register node class handler
|
@@ -23,20 +21,15 @@ module Mutant
|
|
23
21
|
# @return [undefined]
|
24
22
|
def self.handle(*types)
|
25
23
|
types.each do |type|
|
26
|
-
REGISTRY.register(type, self)
|
24
|
+
self::REGISTRY.register(type, self)
|
27
25
|
end
|
28
26
|
end
|
29
27
|
private_class_method :handle
|
30
28
|
|
31
|
-
#
|
29
|
+
# Return output
|
32
30
|
#
|
33
|
-
# @return [
|
34
|
-
attr_reader :
|
35
|
-
|
36
|
-
# Parent context of input
|
37
|
-
#
|
38
|
-
# @return [Object]
|
39
|
-
attr_reader :parent
|
31
|
+
# @return [Set<Parser::AST::Node>]
|
32
|
+
attr_reader :output
|
40
33
|
|
41
34
|
private
|
42
35
|
|
@@ -47,10 +40,11 @@ module Mutant
|
|
47
40
|
# @param [#call(node)] block
|
48
41
|
#
|
49
42
|
# @return [undefined]
|
50
|
-
def initialize(
|
51
|
-
|
52
|
-
|
53
|
-
|
43
|
+
def initialize(_input, _parent = nil)
|
44
|
+
super
|
45
|
+
|
46
|
+
@output = Set.new
|
47
|
+
|
54
48
|
dispatch
|
55
49
|
end
|
56
50
|
|
@@ -60,16 +54,7 @@ module Mutant
|
|
60
54
|
#
|
61
55
|
# @return [Boolean]
|
62
56
|
def new?(object)
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
# Add object to guarded values
|
67
|
-
#
|
68
|
-
# @param [Object] object
|
69
|
-
#
|
70
|
-
# @return [undefined]
|
71
|
-
def guard(object)
|
72
|
-
@seen << object
|
57
|
+
!object.eql?(input)
|
73
58
|
end
|
74
59
|
|
75
60
|
# Dispatch node generations
|
@@ -85,26 +70,14 @@ module Mutant
|
|
85
70
|
def emit(object)
|
86
71
|
return unless new?(object)
|
87
72
|
|
88
|
-
|
89
|
-
|
90
|
-
emit!(object)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Call block with node
|
94
|
-
#
|
95
|
-
# @param [Parser::AST::Node] node
|
96
|
-
#
|
97
|
-
# @return [self]
|
98
|
-
def emit!(node)
|
99
|
-
@block.call(node)
|
100
|
-
self
|
73
|
+
output << object
|
101
74
|
end
|
102
75
|
|
103
76
|
# Run input with mutator
|
104
77
|
#
|
105
78
|
# @return [undefined]
|
106
79
|
def run(mutator)
|
107
|
-
mutator.
|
80
|
+
mutator.call(input).each(&method(:emit))
|
108
81
|
end
|
109
82
|
|
110
83
|
# Shortcut to create a new unfrozen duplicate of input
|