mutant 0.5.26 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|