mutant 0.5.26 → 0.6.0
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/.rspec +1 -0
- data/.travis.yml +1 -0
- data/Changelog.md +16 -3
- data/Gemfile +0 -2
- data/Gemfile.devtools +2 -2
- data/README.md +9 -15
- data/bin/mutant +0 -1
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/mutant.yml +1 -1
- data/config/reek.yml +14 -11
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +22 -21
- data/lib/mutant/ast.rb +47 -0
- data/lib/mutant/cli.rb +7 -4
- data/lib/mutant/config.rb +1 -0
- data/lib/mutant/context.rb +1 -1
- data/lib/mutant/diff.rb +38 -7
- data/lib/mutant/env.rb +22 -3
- data/lib/mutant/expression.rb +15 -4
- data/lib/mutant/integration.rb +1 -1
- data/lib/mutant/isolation.rb +2 -4
- data/lib/mutant/matcher.rb +1 -1
- data/lib/mutant/matcher/method.rb +1 -1
- data/lib/mutant/matcher/method/singleton.rb +1 -1
- data/lib/mutant/matcher/methods.rb +0 -2
- data/lib/mutant/meta/example.rb +0 -2
- data/lib/mutant/meta/example/dsl.rb +1 -1
- data/lib/mutant/mutator.rb +1 -1
- data/lib/mutant/mutator/node.rb +3 -3
- data/lib/mutant/mutator/node/begin.rb +1 -1
- data/lib/mutant/mutator/node/block.rb +16 -3
- data/lib/mutant/mutator/node/if.rb +1 -1
- data/lib/mutant/mutator/node/literal/fixnum.rb +1 -1
- data/lib/mutant/mutator/node/resbody.rb +0 -2
- data/lib/mutant/mutator/node/send.rb +17 -7
- data/lib/mutant/mutator/node/send/index.rb +0 -2
- data/lib/mutant/mutator/registry.rb +1 -1
- data/lib/mutant/mutator/util.rb +1 -1
- data/lib/mutant/mutator/util/array.rb +1 -1
- data/lib/mutant/reporter.rb +13 -3
- data/lib/mutant/reporter/cli.rb +54 -8
- data/lib/mutant/reporter/cli/format.rb +197 -0
- data/lib/mutant/reporter/cli/printer.rb +402 -22
- data/lib/mutant/reporter/cli/tput.rb +27 -0
- data/lib/mutant/reporter/null.rb +4 -34
- data/lib/mutant/reporter/trace.rb +6 -38
- data/lib/mutant/result.rb +44 -56
- data/lib/mutant/runner.rb +99 -52
- data/lib/mutant/runner/collector.rb +134 -0
- data/lib/mutant/subject/method/instance.rb +12 -4
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warning_filter.rb +0 -2
- data/lib/mutant/zombifier/file.rb +1 -1
- data/meta/block.rb +17 -1
- data/meta/send.rb +123 -1
- data/mutant-rspec.gemspec +3 -3
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/corpus_spec.rb +4 -195
- data/spec/integration/mutant/null_spec.rb +1 -3
- data/spec/integration/mutant/rspec_spec.rb +1 -3
- data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -3
- data/spec/integration/mutant/zombie_spec.rb +1 -3
- data/spec/integrations.yml +7 -0
- data/spec/shared/method_matcher_behavior.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/compress_helper.rb +1 -0
- data/spec/support/corpus.rb +239 -0
- data/spec/support/mutation_verifier.rb +2 -4
- data/spec/unit/mutant/cli_spec.rb +20 -13
- data/spec/unit/mutant/context/root_spec.rb +1 -3
- data/spec/unit/mutant/context/scope/root_spec.rb +1 -3
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +1 -3
- data/spec/unit/mutant/diff_spec.rb +37 -19
- data/spec/unit/mutant/expression/method_spec.rb +5 -7
- data/spec/unit/mutant/expression/methods_spec.rb +5 -7
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +6 -8
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +6 -7
- data/spec/unit/mutant/expression_spec.rb +14 -5
- data/spec/unit/mutant/integration_spec.rb +14 -3
- data/spec/unit/mutant/isolation_spec.rb +2 -4
- data/spec/unit/mutant/loader/eval_spec.rb +1 -3
- data/spec/unit/mutant/matcher/chain_spec.rb +1 -3
- data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +21 -0
- data/spec/unit/mutant/matcher/compiler_spec.rb +28 -3
- data/spec/unit/mutant/matcher/filter_spec.rb +1 -3
- data/spec/unit/mutant/matcher/method/instance_spec.rb +3 -5
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +22 -4
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +7 -6
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +4 -6
- data/spec/unit/mutant/matcher/namespace_spec.rb +1 -3
- data/spec/unit/mutant/matcher/null_spec.rb +1 -3
- data/spec/unit/mutant/mutation_spec.rb +1 -3
- data/spec/unit/mutant/mutator/node_spec.rb +1 -3
- data/spec/unit/mutant/reporter/cli_spec.rb +444 -206
- data/spec/unit/mutant/reporter/null_spec.rb +1 -3
- data/spec/unit/mutant/require_highjack_spec.rb +1 -3
- data/spec/unit/mutant/runner_spec.rb +42 -28
- data/spec/unit/mutant/subject/context_spec.rb +1 -3
- data/spec/unit/mutant/subject/method/instance_spec.rb +27 -19
- data/spec/unit/mutant/subject/method/singleton_spec.rb +49 -17
- data/spec/unit/mutant/subject_spec.rb +1 -3
- data/spec/unit/mutant/test_spec.rb +1 -3
- data/spec/unit/mutant/warning_expectation.rb +1 -3
- data/spec/unit/mutant/warning_filter_spec.rb +1 -3
- data/spec/unit/mutant_spec.rb +13 -3
- data/test_app/Gemfile.devtools +2 -2
- data/test_app/spec/unit/test_app/literal/string_spec.rb +1 -1
- metadata +10 -21
- data/lib/mutant/matcher/method/finder.rb +0 -72
- data/lib/mutant/reporter/cli/progress.rb +0 -10
- data/lib/mutant/reporter/cli/progress/config.rb +0 -30
- data/lib/mutant/reporter/cli/progress/env.rb +0 -30
- data/lib/mutant/reporter/cli/progress/noop.rb +0 -27
- data/lib/mutant/reporter/cli/progress/result.rb +0 -12
- data/lib/mutant/reporter/cli/progress/result/mutation.rb +0 -45
- data/lib/mutant/reporter/cli/progress/result/subject.rb +0 -54
- data/lib/mutant/reporter/cli/progress/subject.rb +0 -27
- data/lib/mutant/reporter/cli/registry.rb +0 -81
- data/lib/mutant/reporter/cli/report.rb +0 -10
- data/lib/mutant/reporter/cli/report/env.rb +0 -92
- data/lib/mutant/reporter/cli/report/mutation.rb +0 -103
- data/lib/mutant/reporter/cli/report/subject.rb +0 -32
- data/lib/mutant/reporter/cli/report/test.rb +0 -28
- data/lib/mutant/walker.rb +0 -53
- data/spec/shared/mutator_behavior.rb +0 -55
data/lib/mutant/reporter.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Mutant
|
|
2
2
|
# Abstract base class for reporters
|
|
3
3
|
class Reporter
|
|
4
|
-
include
|
|
4
|
+
include AbstractType
|
|
5
5
|
|
|
6
6
|
# Write warning message
|
|
7
7
|
#
|
|
@@ -13,9 +13,19 @@ module Mutant
|
|
|
13
13
|
#
|
|
14
14
|
abstract_method :warn
|
|
15
15
|
|
|
16
|
-
# Report
|
|
16
|
+
# Report start
|
|
17
17
|
#
|
|
18
|
-
# @param [
|
|
18
|
+
# @param [Env] env
|
|
19
|
+
#
|
|
20
|
+
# @return [self]
|
|
21
|
+
#
|
|
22
|
+
# @api private
|
|
23
|
+
#
|
|
24
|
+
abstract_method :start
|
|
25
|
+
|
|
26
|
+
# Report collector state
|
|
27
|
+
#
|
|
28
|
+
# @param [Runner::Collector] collector
|
|
19
29
|
#
|
|
20
30
|
# @return [self]
|
|
21
31
|
#
|
data/lib/mutant/reporter/cli.rb
CHANGED
|
@@ -2,18 +2,50 @@ module Mutant
|
|
|
2
2
|
class Reporter
|
|
3
3
|
# Reporter that reports in human readable format
|
|
4
4
|
class CLI < self
|
|
5
|
-
include Concord.new(:output)
|
|
5
|
+
include Concord.new(:output, :format)
|
|
6
|
+
|
|
7
|
+
# Build reporter
|
|
8
|
+
#
|
|
9
|
+
# @param [IO] output
|
|
10
|
+
#
|
|
11
|
+
# @return [Reporter::CLI]
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
#
|
|
15
|
+
def self.build(output)
|
|
16
|
+
tty = output.respond_to?(:tty?) && output.tty?
|
|
17
|
+
format =
|
|
18
|
+
if !Mutant.ci? && tty && Tput::INSTANCE.available
|
|
19
|
+
Format::Framed.new(tty: tty, tput: Tput::INSTANCE)
|
|
20
|
+
else
|
|
21
|
+
Format::Progressive.new(tty: tty)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
new(output, format)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Report start
|
|
28
|
+
#
|
|
29
|
+
# @param [Env] env
|
|
30
|
+
#
|
|
31
|
+
# @api private
|
|
32
|
+
#
|
|
33
|
+
def start(env)
|
|
34
|
+
write(format.start(env))
|
|
35
|
+
self
|
|
36
|
+
end
|
|
6
37
|
|
|
7
38
|
# Report progress object
|
|
8
39
|
#
|
|
9
|
-
# @param [
|
|
40
|
+
# @param [Runner::Collector] collector
|
|
10
41
|
#
|
|
11
42
|
# @return [self]
|
|
12
43
|
#
|
|
13
44
|
# @api private
|
|
14
45
|
#
|
|
15
|
-
def progress(
|
|
16
|
-
|
|
46
|
+
def progress(collector)
|
|
47
|
+
write(format.progress(collector))
|
|
48
|
+
|
|
17
49
|
self
|
|
18
50
|
end
|
|
19
51
|
|
|
@@ -30,19 +62,33 @@ module Mutant
|
|
|
30
62
|
self
|
|
31
63
|
end
|
|
32
64
|
|
|
33
|
-
# Report
|
|
65
|
+
# Report env
|
|
34
66
|
#
|
|
35
|
-
# @param [
|
|
67
|
+
# @param [Result::Env] env
|
|
36
68
|
#
|
|
37
69
|
# @return [self]
|
|
38
70
|
#
|
|
39
71
|
# @api private
|
|
40
72
|
#
|
|
41
|
-
def report(
|
|
42
|
-
|
|
73
|
+
def report(env)
|
|
74
|
+
write(format.report(env))
|
|
43
75
|
self
|
|
44
76
|
end
|
|
45
77
|
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# Write output frame
|
|
81
|
+
#
|
|
82
|
+
# @param [String] frame
|
|
83
|
+
#
|
|
84
|
+
# @return [undefined]
|
|
85
|
+
#
|
|
86
|
+
# @api private
|
|
87
|
+
#
|
|
88
|
+
def write(frame)
|
|
89
|
+
output.write(frame)
|
|
90
|
+
end
|
|
91
|
+
|
|
46
92
|
end # CLI
|
|
47
93
|
end # Reporter
|
|
48
94
|
end # Mutant
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
module Mutant
|
|
2
|
+
class Reporter
|
|
3
|
+
class CLI
|
|
4
|
+
# CLI output format
|
|
5
|
+
class Format
|
|
6
|
+
include AbstractType, Anima.new(:tty)
|
|
7
|
+
|
|
8
|
+
# Return start representation
|
|
9
|
+
#
|
|
10
|
+
# @param [Env] env
|
|
11
|
+
#
|
|
12
|
+
# @return [String]
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
#
|
|
16
|
+
abstract_method :start
|
|
17
|
+
|
|
18
|
+
# Return progress representation
|
|
19
|
+
#
|
|
20
|
+
# @param [Runner::Collector] collector
|
|
21
|
+
#
|
|
22
|
+
# @return [String]
|
|
23
|
+
#
|
|
24
|
+
# @api private
|
|
25
|
+
#
|
|
26
|
+
abstract_method :progress
|
|
27
|
+
|
|
28
|
+
# Format result
|
|
29
|
+
#
|
|
30
|
+
# @param [Result::Env] env
|
|
31
|
+
#
|
|
32
|
+
# @return [String]
|
|
33
|
+
#
|
|
34
|
+
# @api private
|
|
35
|
+
#
|
|
36
|
+
def report(env)
|
|
37
|
+
format(Printer::EnvResult, env)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Output abstraction to decouple tty? from buffer
|
|
41
|
+
class Output
|
|
42
|
+
include Concord.new(:tty, :buffer)
|
|
43
|
+
|
|
44
|
+
# Test if output is a tty
|
|
45
|
+
#
|
|
46
|
+
# @return [Boolean]
|
|
47
|
+
#
|
|
48
|
+
# @api private
|
|
49
|
+
#
|
|
50
|
+
def tty?
|
|
51
|
+
@tty
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
[:puts, :write].each do |name|
|
|
55
|
+
define_method(name) do |*args, &block|
|
|
56
|
+
buffer.public_send(name, *args, &block)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end # Output
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Format object with printer
|
|
64
|
+
#
|
|
65
|
+
# @param [Class:Printer] printer
|
|
66
|
+
# @param [Object] object
|
|
67
|
+
#
|
|
68
|
+
# @return [String]
|
|
69
|
+
#
|
|
70
|
+
# @api private
|
|
71
|
+
#
|
|
72
|
+
def format(printer, object)
|
|
73
|
+
buffer = new_buffer
|
|
74
|
+
printer.run(Output.new(tty, buffer), object)
|
|
75
|
+
buffer.rewind
|
|
76
|
+
buffer.read
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Format for progressive non rewindable output
|
|
80
|
+
class Progressive < self
|
|
81
|
+
|
|
82
|
+
# Return start representation
|
|
83
|
+
#
|
|
84
|
+
# @return [String]
|
|
85
|
+
#
|
|
86
|
+
# @api private
|
|
87
|
+
#
|
|
88
|
+
def start(env)
|
|
89
|
+
format(Printer::Config, env.config)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Return progress representation
|
|
93
|
+
#
|
|
94
|
+
# @return [String]
|
|
95
|
+
#
|
|
96
|
+
# @api private
|
|
97
|
+
#
|
|
98
|
+
def progress(collector)
|
|
99
|
+
last_mutation_result = collector.last_mutation_result
|
|
100
|
+
return EMPTY_STRING unless last_mutation_result
|
|
101
|
+
format(Printer::MutationProgressResult, last_mutation_result)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
# Return new buffer
|
|
107
|
+
#
|
|
108
|
+
# @return [StringIO]
|
|
109
|
+
#
|
|
110
|
+
# @api private
|
|
111
|
+
#
|
|
112
|
+
def new_buffer
|
|
113
|
+
StringIO.new
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end # Progressive
|
|
117
|
+
|
|
118
|
+
# Format for framed rewindable output
|
|
119
|
+
class Framed < self
|
|
120
|
+
include anima.add(:tput)
|
|
121
|
+
|
|
122
|
+
BUFFER_FLAGS = 'a+'.freeze
|
|
123
|
+
|
|
124
|
+
# Rate per second progress report fires
|
|
125
|
+
OUTPUT_RATE = 1.0 / 20
|
|
126
|
+
|
|
127
|
+
# Initialize object
|
|
128
|
+
#
|
|
129
|
+
# @return [undefined]
|
|
130
|
+
#
|
|
131
|
+
# @api private
|
|
132
|
+
#
|
|
133
|
+
def initialize(*)
|
|
134
|
+
super
|
|
135
|
+
@last_frame = nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Format start
|
|
139
|
+
#
|
|
140
|
+
# @param [Env] env
|
|
141
|
+
#
|
|
142
|
+
# @return [String]
|
|
143
|
+
#
|
|
144
|
+
# @api private
|
|
145
|
+
#
|
|
146
|
+
def start(_env)
|
|
147
|
+
tput.prepare
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Format progress
|
|
151
|
+
#
|
|
152
|
+
# @param [Runner::Collector] collector
|
|
153
|
+
#
|
|
154
|
+
# @return [String]
|
|
155
|
+
#
|
|
156
|
+
# @api private
|
|
157
|
+
#
|
|
158
|
+
def progress(collector)
|
|
159
|
+
throttle do
|
|
160
|
+
format(Printer::Collector, collector)
|
|
161
|
+
end.to_s
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
# Return new buffer
|
|
167
|
+
#
|
|
168
|
+
# @return [StringIO]
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
#
|
|
172
|
+
def new_buffer
|
|
173
|
+
# For some reason this raises an Ernno::EACCESS errror:
|
|
174
|
+
#
|
|
175
|
+
# StringIO.new(Tput::INSTANCE.restore, BUFFER_FLAGS)
|
|
176
|
+
#
|
|
177
|
+
buffer = StringIO.new
|
|
178
|
+
buffer << tput.restore
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Call block throttled
|
|
182
|
+
#
|
|
183
|
+
# @return [self]
|
|
184
|
+
#
|
|
185
|
+
# @api private
|
|
186
|
+
#
|
|
187
|
+
def throttle
|
|
188
|
+
now = Time.now
|
|
189
|
+
return if @last_frame && (now - @last_frame) < OUTPUT_RATE
|
|
190
|
+
yield.tap { @last_frame = now }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
end # Framed
|
|
194
|
+
end # Format
|
|
195
|
+
end # CLI
|
|
196
|
+
end # Reporter
|
|
197
|
+
end # Mutant
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module Mutant
|
|
2
2
|
class Reporter
|
|
3
3
|
class CLI
|
|
4
|
-
|
|
5
4
|
# CLI runner status printer base class
|
|
6
5
|
class Printer
|
|
7
6
|
include AbstractType, Delegator, Adamantium::Flat, Concord.new(:output, :object)
|
|
@@ -18,9 +17,7 @@ module Mutant
|
|
|
18
17
|
# @api private
|
|
19
18
|
#
|
|
20
19
|
def self.run(output, object)
|
|
21
|
-
|
|
22
|
-
handler.new(output, object).run
|
|
23
|
-
self
|
|
20
|
+
new(output, object).run
|
|
24
21
|
end
|
|
25
22
|
|
|
26
23
|
# Run printer
|
|
@@ -45,26 +42,30 @@ module Mutant
|
|
|
45
42
|
|
|
46
43
|
# Visit a collection of objects
|
|
47
44
|
#
|
|
45
|
+
# @return [Class::Printer] printer
|
|
48
46
|
# @return [Enumerable<Object>] collection
|
|
49
47
|
#
|
|
50
48
|
# @return [undefined]
|
|
51
49
|
#
|
|
52
50
|
# @api private
|
|
53
51
|
#
|
|
54
|
-
def visit_collection(collection)
|
|
55
|
-
collection.each
|
|
52
|
+
def visit_collection(printer, collection)
|
|
53
|
+
collection.each do |object|
|
|
54
|
+
visit(printer, object)
|
|
55
|
+
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Visit object
|
|
59
59
|
#
|
|
60
|
+
# @param [Class::Printer] printer
|
|
60
61
|
# @param [Object] object
|
|
61
62
|
#
|
|
62
63
|
# @return [undefined]
|
|
63
64
|
#
|
|
64
65
|
# @api private
|
|
65
66
|
#
|
|
66
|
-
def visit(object)
|
|
67
|
-
|
|
67
|
+
def visit(printer, object)
|
|
68
|
+
printer.run(output, object)
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
# Print an info line to output
|
|
@@ -93,7 +94,7 @@ module Mutant
|
|
|
93
94
|
#
|
|
94
95
|
# @api private
|
|
95
96
|
#
|
|
96
|
-
def puts(string
|
|
97
|
+
def puts(string)
|
|
97
98
|
output.puts(string)
|
|
98
99
|
end
|
|
99
100
|
|
|
@@ -107,16 +108,6 @@ module Mutant
|
|
|
107
108
|
object.success?
|
|
108
109
|
end
|
|
109
110
|
|
|
110
|
-
# Test if output can be colored
|
|
111
|
-
#
|
|
112
|
-
# @return [Boolean]
|
|
113
|
-
#
|
|
114
|
-
# @api private
|
|
115
|
-
#
|
|
116
|
-
def color?
|
|
117
|
-
tty?
|
|
118
|
-
end
|
|
119
|
-
|
|
120
111
|
# Colorize message
|
|
121
112
|
#
|
|
122
113
|
# @param [Color] color
|
|
@@ -133,17 +124,406 @@ module Mutant
|
|
|
133
124
|
color.format(message)
|
|
134
125
|
end
|
|
135
126
|
|
|
136
|
-
# Test
|
|
127
|
+
# Test if output is a tty
|
|
137
128
|
#
|
|
138
129
|
# @return [Boolean]
|
|
139
130
|
#
|
|
140
131
|
# @api private
|
|
141
132
|
#
|
|
142
133
|
def tty?
|
|
143
|
-
output.
|
|
134
|
+
output.tty?
|
|
144
135
|
end
|
|
145
|
-
memoize :tty?
|
|
146
136
|
|
|
137
|
+
# Test if output can be colored
|
|
138
|
+
#
|
|
139
|
+
# @return [Boolean]
|
|
140
|
+
#
|
|
141
|
+
# @api private
|
|
142
|
+
#
|
|
143
|
+
alias_method :color?, :tty?
|
|
144
|
+
|
|
145
|
+
# Printer for run collector
|
|
146
|
+
class Collector < self
|
|
147
|
+
|
|
148
|
+
# Print progress for collector
|
|
149
|
+
#
|
|
150
|
+
# @return [self]
|
|
151
|
+
#
|
|
152
|
+
# @api private
|
|
153
|
+
#
|
|
154
|
+
def run
|
|
155
|
+
visit(EnvProgress, object.result)
|
|
156
|
+
active_subject_results = object.active_subject_results
|
|
157
|
+
info('Active subjects: %d', active_subject_results.length)
|
|
158
|
+
visit_collection(SubjectProgress, active_subject_results)
|
|
159
|
+
self
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
end # Collector
|
|
163
|
+
|
|
164
|
+
# Progress printer for configuration
|
|
165
|
+
class Config < self
|
|
166
|
+
|
|
167
|
+
# Report configuration
|
|
168
|
+
#
|
|
169
|
+
# @param [Mutant::Config] config
|
|
170
|
+
#
|
|
171
|
+
# @return [self]
|
|
172
|
+
#
|
|
173
|
+
# @api private
|
|
174
|
+
#
|
|
175
|
+
def run
|
|
176
|
+
info 'Mutant configuration:'
|
|
177
|
+
info 'Matcher: %s', object.matcher_config.inspect
|
|
178
|
+
info 'Integration: %s', object.integration.name
|
|
179
|
+
info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
|
|
180
|
+
info 'Processes: %d', object.processes
|
|
181
|
+
info 'Includes: %s', object.includes.inspect
|
|
182
|
+
info 'Requires: %s', object.requires.inspect
|
|
183
|
+
self
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
end # Config
|
|
187
|
+
|
|
188
|
+
# Env progress printer
|
|
189
|
+
class EnvProgress < self
|
|
190
|
+
|
|
191
|
+
delegate(
|
|
192
|
+
:coverage,
|
|
193
|
+
:amount_subjects,
|
|
194
|
+
:amount_mutations,
|
|
195
|
+
:amount_mutations_alive,
|
|
196
|
+
:amount_mutations_killed,
|
|
197
|
+
:runtime,
|
|
198
|
+
:killtime,
|
|
199
|
+
:overhead,
|
|
200
|
+
:env
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Run printer
|
|
204
|
+
#
|
|
205
|
+
# @return [self]
|
|
206
|
+
#
|
|
207
|
+
# @api private
|
|
208
|
+
#
|
|
209
|
+
def run
|
|
210
|
+
visit(Config, env.config)
|
|
211
|
+
info 'Available Subjects: %s', amount_subjects
|
|
212
|
+
info 'Subjects: %s', amount_subjects
|
|
213
|
+
info 'Mutations: %s', amount_mutations
|
|
214
|
+
info 'Kills: %s', amount_mutations_killed
|
|
215
|
+
info 'Alive: %s', amount_mutations_alive
|
|
216
|
+
info 'Runtime: %0.2fs', runtime
|
|
217
|
+
info 'Killtime: %0.2fs', killtime
|
|
218
|
+
info 'Overhead: %0.2f%%', overhead_percent
|
|
219
|
+
status 'Coverage: %0.2f%%', coverage_percent
|
|
220
|
+
status 'Expected: %0.2f%%', env.config.expected_coverage
|
|
221
|
+
self
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
|
|
226
|
+
# Return coverage percent
|
|
227
|
+
#
|
|
228
|
+
# @return [Float]
|
|
229
|
+
#
|
|
230
|
+
# @api private
|
|
231
|
+
#
|
|
232
|
+
def coverage_percent
|
|
233
|
+
coverage * 100
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Return overhead percent
|
|
237
|
+
#
|
|
238
|
+
# @return [Float]
|
|
239
|
+
#
|
|
240
|
+
# @api private
|
|
241
|
+
#
|
|
242
|
+
def overhead_percent
|
|
243
|
+
(overhead / killtime) * 100
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
end # EnvProgress
|
|
247
|
+
|
|
248
|
+
# Full env result reporter
|
|
249
|
+
class EnvResult < self
|
|
250
|
+
|
|
251
|
+
delegate(:failed_subject_results)
|
|
252
|
+
|
|
253
|
+
# Run printer
|
|
254
|
+
#
|
|
255
|
+
# @return [self]
|
|
256
|
+
#
|
|
257
|
+
# @api private
|
|
258
|
+
#
|
|
259
|
+
def run
|
|
260
|
+
visit_collection(SubjectResult, failed_subject_results)
|
|
261
|
+
visit(EnvProgress, object)
|
|
262
|
+
self
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
end # EnvResult
|
|
266
|
+
|
|
267
|
+
# Subject report printer
|
|
268
|
+
class SubjectResult < self
|
|
269
|
+
|
|
270
|
+
delegate :subject, :failed_mutations
|
|
271
|
+
|
|
272
|
+
# Run report printer
|
|
273
|
+
#
|
|
274
|
+
# @return [self]
|
|
275
|
+
#
|
|
276
|
+
# @api private
|
|
277
|
+
#
|
|
278
|
+
def run
|
|
279
|
+
status(subject.identification)
|
|
280
|
+
subject.tests.each do |test|
|
|
281
|
+
puts("- #{test.identification}")
|
|
282
|
+
end
|
|
283
|
+
visit_collection(MutationResult, object.alive_mutation_results)
|
|
284
|
+
self
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
end # Subject
|
|
288
|
+
|
|
289
|
+
# Printer for mutation progress results
|
|
290
|
+
class MutationProgressResult < self
|
|
291
|
+
|
|
292
|
+
SUCCESS = '.'.freeze
|
|
293
|
+
FAILURE = 'F'.freeze
|
|
294
|
+
|
|
295
|
+
# Run printer
|
|
296
|
+
#
|
|
297
|
+
# @return [self]
|
|
298
|
+
#
|
|
299
|
+
# @api private
|
|
300
|
+
#
|
|
301
|
+
def run
|
|
302
|
+
char(success? ? SUCCESS : FAILURE)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
private
|
|
306
|
+
|
|
307
|
+
# Write colorized char
|
|
308
|
+
#
|
|
309
|
+
# @param [String] char
|
|
310
|
+
#
|
|
311
|
+
# @return [undefined]
|
|
312
|
+
#
|
|
313
|
+
# @api private
|
|
314
|
+
#
|
|
315
|
+
def char(char)
|
|
316
|
+
output.write(colorize(status_color, char))
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
end # MutationProgressResult
|
|
320
|
+
|
|
321
|
+
# Reporter for subject progress
|
|
322
|
+
class SubjectProgress < self
|
|
323
|
+
|
|
324
|
+
FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
|
|
325
|
+
|
|
326
|
+
delegate(
|
|
327
|
+
:subject,
|
|
328
|
+
:coverage,
|
|
329
|
+
:runtime,
|
|
330
|
+
:amount_mutations_killed,
|
|
331
|
+
:amount_mutations,
|
|
332
|
+
:amount_mutation_results,
|
|
333
|
+
:killtime,
|
|
334
|
+
:overhead
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Run printer
|
|
338
|
+
#
|
|
339
|
+
# @return [self]
|
|
340
|
+
#
|
|
341
|
+
# @api private
|
|
342
|
+
#
|
|
343
|
+
def run
|
|
344
|
+
puts("#{subject.identification} mutations: #{amount_mutations}")
|
|
345
|
+
print_tests
|
|
346
|
+
print_mutation_results
|
|
347
|
+
print_progress_bar_finish
|
|
348
|
+
print_stats
|
|
349
|
+
self
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
private
|
|
353
|
+
|
|
354
|
+
# Print stats
|
|
355
|
+
#
|
|
356
|
+
# @return [undefined]
|
|
357
|
+
#
|
|
358
|
+
# @api private
|
|
359
|
+
#
|
|
360
|
+
def print_stats
|
|
361
|
+
status(
|
|
362
|
+
FORMAT,
|
|
363
|
+
amount_mutations_killed,
|
|
364
|
+
amount_mutations,
|
|
365
|
+
coverage * 100,
|
|
366
|
+
killtime,
|
|
367
|
+
runtime,
|
|
368
|
+
overhead
|
|
369
|
+
)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Print tests
|
|
373
|
+
#
|
|
374
|
+
# @return [undefined]
|
|
375
|
+
#
|
|
376
|
+
# @api private
|
|
377
|
+
#
|
|
378
|
+
def print_tests
|
|
379
|
+
subject.tests.each do |test|
|
|
380
|
+
puts "- #{test.identification}"
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Print progress bar finish
|
|
385
|
+
#
|
|
386
|
+
# @return [undefined]
|
|
387
|
+
#
|
|
388
|
+
# @api private
|
|
389
|
+
#
|
|
390
|
+
def print_progress_bar_finish
|
|
391
|
+
puts(NL) unless amount_mutation_results.zero?
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Print mutation results
|
|
395
|
+
#
|
|
396
|
+
# @return [undefined]
|
|
397
|
+
#
|
|
398
|
+
# @api private
|
|
399
|
+
#
|
|
400
|
+
def print_mutation_results
|
|
401
|
+
visit_collection(MutationProgressResult, object.mutation_results)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
end # Subject
|
|
405
|
+
|
|
406
|
+
# Reporter for mutation results
|
|
407
|
+
class MutationResult < self
|
|
408
|
+
|
|
409
|
+
delegate :mutation, :failed_test_results
|
|
410
|
+
|
|
411
|
+
DIFF_ERROR_MESSAGE = 'BUG: Mutation NOT resulted in exactly one diff. Please report a reproduction!'.freeze
|
|
412
|
+
|
|
413
|
+
MAP = {
|
|
414
|
+
Mutant::Mutation::Evil => :evil_details,
|
|
415
|
+
Mutant::Mutation::Neutral => :neutral_details,
|
|
416
|
+
Mutant::Mutation::Noop => :noop_details
|
|
417
|
+
}.freeze
|
|
418
|
+
|
|
419
|
+
NEUTRAL_MESSAGE =
|
|
420
|
+
"--- Neutral failure ---\n" \
|
|
421
|
+
"Original code was inserted unmutated. And the test did NOT PASS.\n" \
|
|
422
|
+
"Your tests do not pass initially or you found a bug in mutant / unparser.\n" \
|
|
423
|
+
"Subject AST:\n" \
|
|
424
|
+
"%s\n" \
|
|
425
|
+
"Unparsed Source:\n" \
|
|
426
|
+
"%s\n" \
|
|
427
|
+
"Test Reports: %d\n"
|
|
428
|
+
|
|
429
|
+
NOOP_MESSAGE =
|
|
430
|
+
"---- Noop failure -----\n" \
|
|
431
|
+
"No code was inserted. And the test did NOT PASS.\n" \
|
|
432
|
+
"This is typically a problem of your specs not passing unmutated.\n" \
|
|
433
|
+
"Test Reports: %d\n"
|
|
434
|
+
|
|
435
|
+
FOOTER = '-----------------------'.freeze
|
|
436
|
+
|
|
437
|
+
# Run report printer
|
|
438
|
+
#
|
|
439
|
+
# @return [self]
|
|
440
|
+
#
|
|
441
|
+
# @api private
|
|
442
|
+
#
|
|
443
|
+
def run
|
|
444
|
+
puts(mutation.identification)
|
|
445
|
+
print_details
|
|
446
|
+
puts(FOOTER)
|
|
447
|
+
self
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
private
|
|
451
|
+
|
|
452
|
+
# Return details
|
|
453
|
+
#
|
|
454
|
+
# @return [undefined]
|
|
455
|
+
#
|
|
456
|
+
# @api private
|
|
457
|
+
#
|
|
458
|
+
def print_details
|
|
459
|
+
send(MAP.fetch(mutation.class))
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Return evil details
|
|
463
|
+
#
|
|
464
|
+
# @return [String]
|
|
465
|
+
#
|
|
466
|
+
# @api private
|
|
467
|
+
#
|
|
468
|
+
def evil_details
|
|
469
|
+
original, current = mutation.original_source, mutation.source
|
|
470
|
+
diff = Mutant::Diff.build(original, current)
|
|
471
|
+
diff = color? ? diff.colorized_diff : diff.diff
|
|
472
|
+
puts(diff || ['Original source:', original, 'Mutated Source:', current, DIFF_ERROR_MESSAGE])
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Noop details
|
|
476
|
+
#
|
|
477
|
+
# @return [String]
|
|
478
|
+
#
|
|
479
|
+
# @api private
|
|
480
|
+
#
|
|
481
|
+
def noop_details
|
|
482
|
+
info(NOOP_MESSAGE, failed_test_results.length)
|
|
483
|
+
visit_failed_test_results
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Neutral details
|
|
487
|
+
#
|
|
488
|
+
# @return [String]
|
|
489
|
+
#
|
|
490
|
+
# @api private
|
|
491
|
+
#
|
|
492
|
+
def neutral_details
|
|
493
|
+
info(NEUTRAL_MESSAGE, mutation.subject.node.inspect, mutation.source, failed_test_results.length)
|
|
494
|
+
visit_failed_test_results
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Visit failed test results
|
|
498
|
+
#
|
|
499
|
+
# @return [undefined]
|
|
500
|
+
#
|
|
501
|
+
# @api private
|
|
502
|
+
#
|
|
503
|
+
def visit_failed_test_results
|
|
504
|
+
visit_collection(TestResult, failed_test_results)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
end # MutationResult
|
|
508
|
+
|
|
509
|
+
# Test result reporter
|
|
510
|
+
class TestResult < self
|
|
511
|
+
|
|
512
|
+
delegate :test, :runtime
|
|
513
|
+
|
|
514
|
+
# Run test result reporter
|
|
515
|
+
#
|
|
516
|
+
# @return [self]
|
|
517
|
+
#
|
|
518
|
+
# @api private
|
|
519
|
+
#
|
|
520
|
+
def run
|
|
521
|
+
status('- %s / runtime: %s', test.identification, object.runtime)
|
|
522
|
+
puts('Test Output:')
|
|
523
|
+
puts(object.output)
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
end # TestResult
|
|
147
527
|
end # Printer
|
|
148
528
|
end # CLI
|
|
149
529
|
end # Reporter
|