mutant 0.11.28 → 0.11.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/ast.rb +8 -9
- data/lib/mutant/bootstrap.rb +39 -13
- data/lib/mutant/cli/command/environment/test.rb +38 -1
- data/lib/mutant/cli/command/environment.rb +11 -0
- data/lib/mutant/context.rb +31 -61
- data/lib/mutant/env.rb +8 -1
- data/lib/mutant/expression/method.rb +4 -1
- data/lib/mutant/expression/methods.rb +4 -1
- data/lib/mutant/expression/namespace.rb +4 -4
- data/lib/mutant/hooks.rb +1 -0
- data/lib/mutant/integration/null.rb +1 -0
- data/lib/mutant/integration.rb +5 -1
- data/lib/mutant/matcher/descendants.rb +1 -1
- data/lib/mutant/matcher/method/instance.rb +5 -4
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/matcher/method/singleton.rb +1 -1
- data/lib/mutant/matcher/method.rb +30 -4
- data/lib/mutant/matcher/methods.rb +8 -7
- data/lib/mutant/matcher/namespace.rb +1 -1
- data/lib/mutant/meta/example.rb +12 -2
- data/lib/mutant/mutation/runner/sink.rb +7 -3
- data/lib/mutant/mutation/runner.rb +2 -3
- data/lib/mutant/parallel/connection.rb +178 -0
- data/lib/mutant/parallel/pipe.rb +39 -0
- data/lib/mutant/parallel/worker.rb +42 -14
- data/lib/mutant/parallel.rb +18 -7
- data/lib/mutant/reporter/cli/format.rb +19 -2
- data/lib/mutant/reporter/cli/printer/test.rb +138 -0
- data/lib/mutant/reporter/cli.rb +33 -4
- data/lib/mutant/reporter.rb +22 -1
- data/lib/mutant/result.rb +53 -2
- data/lib/mutant/scope.rb +41 -1
- data/lib/mutant/subject/method/instance.rb +3 -2
- data/lib/mutant/subject/method/metaclass.rb +1 -1
- data/lib/mutant/subject/method/singleton.rb +2 -2
- data/lib/mutant/subject/method.rb +1 -1
- data/lib/mutant/test/runner/sink.rb +51 -0
- data/lib/mutant/test/runner.rb +62 -0
- data/lib/mutant/timer.rb +9 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +2 -0
- data/lib/mutant.rb +7 -1
- metadata +9 -19
- data/lib/mutant/pipe.rb +0 -96
data/lib/mutant/meta/example.rb
CHANGED
@@ -39,8 +39,9 @@ module Mutant
|
|
39
39
|
# @return [Context]
|
40
40
|
def context
|
41
41
|
Context.new(
|
42
|
-
|
43
|
-
|
42
|
+
constant_scope: Context::ConstantScope::None.new,
|
43
|
+
scope: scope,
|
44
|
+
source_path: location.path
|
44
45
|
)
|
45
46
|
end
|
46
47
|
|
@@ -65,6 +66,15 @@ module Mutant
|
|
65
66
|
end
|
66
67
|
memoize :generated
|
67
68
|
|
69
|
+
private
|
70
|
+
|
71
|
+
def scope
|
72
|
+
Scope.new(
|
73
|
+
expression: Expression::Namespace::Exact.new(scope_name: 'Object'),
|
74
|
+
raw: Object
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
68
78
|
end # Example
|
69
79
|
end # Meta
|
70
80
|
end # Mutant
|
@@ -4,6 +4,8 @@ module Mutant
|
|
4
4
|
class Mutation
|
5
5
|
module Runner
|
6
6
|
class Sink
|
7
|
+
include Parallel::Sink
|
8
|
+
|
7
9
|
include Anima.new(:env)
|
8
10
|
|
9
11
|
# Initialize object
|
@@ -35,11 +37,13 @@ module Mutant
|
|
35
37
|
|
36
38
|
# Handle mutation finish
|
37
39
|
#
|
38
|
-
# @param [
|
40
|
+
# @param [Parallel::Response] response
|
39
41
|
#
|
40
42
|
# @return [self]
|
41
|
-
def
|
42
|
-
|
43
|
+
def response(response)
|
44
|
+
fail response.error if response.error
|
45
|
+
|
46
|
+
mutation_result = mutation_result(response.result)
|
43
47
|
|
44
48
|
subject = mutation_result.mutation.subject
|
45
49
|
|
@@ -15,8 +15,6 @@ module Mutant
|
|
15
15
|
def self.run_mutation_analysis(env)
|
16
16
|
reporter = reporter(env)
|
17
17
|
|
18
|
-
env.world.process_warmup
|
19
|
-
|
20
18
|
env
|
21
19
|
.record(:analysis) { run_driver(reporter, async_driver(env)) }
|
22
20
|
.tap { |result| env.record(:report) { reporter.report(result) } }
|
@@ -24,7 +22,7 @@ module Mutant
|
|
24
22
|
private_class_method :run_mutation_analysis
|
25
23
|
|
26
24
|
def self.async_driver(env)
|
27
|
-
Parallel.async(env.world, mutation_test_config(env))
|
25
|
+
Parallel.async(world: env.world, config: mutation_test_config(env))
|
28
26
|
end
|
29
27
|
private_class_method :async_driver
|
30
28
|
|
@@ -49,6 +47,7 @@ module Mutant
|
|
49
47
|
process_name: 'mutant-worker-process',
|
50
48
|
sink: Sink.new(env: env),
|
51
49
|
source: Parallel::Source::Array.new(jobs: env.mutations.each_index.to_a),
|
50
|
+
timeout: nil,
|
52
51
|
thread_name: 'mutant-worker-thread'
|
53
52
|
)
|
54
53
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module Parallel
|
5
|
+
class Connection
|
6
|
+
include Anima.new(:marshal, :reader, :writer)
|
7
|
+
|
8
|
+
Error = Class.new(RuntimeError)
|
9
|
+
|
10
|
+
HEADER_FORMAT = 'N'
|
11
|
+
HEADER_SIZE = 4
|
12
|
+
MAX_BYTES = (2**32).pred
|
13
|
+
|
14
|
+
class Reader
|
15
|
+
include Anima.new(:deadline, :io, :marshal, :response_reader, :log_reader)
|
16
|
+
|
17
|
+
private(*anima.attribute_names)
|
18
|
+
|
19
|
+
private_class_method :new
|
20
|
+
|
21
|
+
attr_reader :log
|
22
|
+
|
23
|
+
def error
|
24
|
+
@errors.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def result
|
28
|
+
@results.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(*)
|
32
|
+
super
|
33
|
+
|
34
|
+
@buffer = +''
|
35
|
+
@log = +''
|
36
|
+
|
37
|
+
# Array of size max 1 as surrogate for
|
38
|
+
# terrible default nil ivars.
|
39
|
+
@errors = []
|
40
|
+
@lengths = []
|
41
|
+
@results = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.read_response(job:, **attributes)
|
45
|
+
reader = new(**attributes).read_till_final
|
46
|
+
|
47
|
+
Response.new(
|
48
|
+
error: reader.error,
|
49
|
+
job: job,
|
50
|
+
log: reader.log,
|
51
|
+
result: reader.result
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
# rubocop:disable Metrics/MethodLength
|
56
|
+
def read_till_final
|
57
|
+
readers = [response_reader, log_reader]
|
58
|
+
|
59
|
+
until !@results.empty? || error
|
60
|
+
status = deadline.status
|
61
|
+
|
62
|
+
break timeout unless status.ok?
|
63
|
+
|
64
|
+
reads, _others = io.select(readers, nil, nil, status.time_left)
|
65
|
+
|
66
|
+
break timeout unless reads
|
67
|
+
|
68
|
+
reads.each do |ready|
|
69
|
+
if ready.equal?(response_reader)
|
70
|
+
advance_result
|
71
|
+
else
|
72
|
+
advance_log
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
self
|
78
|
+
end
|
79
|
+
# rubocop:enable Metrics/MethodLength
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def timeout
|
84
|
+
@errors << Timeout::Error
|
85
|
+
end
|
86
|
+
|
87
|
+
def advance_result
|
88
|
+
if length
|
89
|
+
if read_buffer(length)
|
90
|
+
@results << marshal.load(@buffer)
|
91
|
+
end
|
92
|
+
elsif read_buffer(HEADER_SIZE)
|
93
|
+
@lengths << Util.one(@buffer.unpack(HEADER_FORMAT))
|
94
|
+
@buffer = +''
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def length
|
99
|
+
@lengths.first
|
100
|
+
end
|
101
|
+
|
102
|
+
def advance_log
|
103
|
+
with_nonblock_read(io: log_reader, max_bytes: 4096, &log.public_method(:<<))
|
104
|
+
end
|
105
|
+
|
106
|
+
def read_buffer(max_bytes)
|
107
|
+
with_nonblock_read(
|
108
|
+
io: response_reader,
|
109
|
+
max_bytes: max_bytes - @buffer.bytesize
|
110
|
+
) do |chunk|
|
111
|
+
@buffer << chunk
|
112
|
+
@buffer.bytesize.equal?(max_bytes)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# rubocop:disable Metrics/MethodLength
|
117
|
+
def with_nonblock_read(io:, max_bytes:)
|
118
|
+
io.binmode
|
119
|
+
|
120
|
+
chunk = io.read_nonblock(max_bytes, exception: false)
|
121
|
+
|
122
|
+
case chunk
|
123
|
+
when nil
|
124
|
+
@errors << EOFError
|
125
|
+
false
|
126
|
+
when String
|
127
|
+
yield chunk
|
128
|
+
else
|
129
|
+
fail "Unexpected nonblocking read return: #{chunk.inspect}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
# rubocop:enable Metrics/MethodLength
|
133
|
+
end
|
134
|
+
|
135
|
+
class Frame
|
136
|
+
include Anima.new(:io)
|
137
|
+
|
138
|
+
def receive_value
|
139
|
+
read(Util.one(read(HEADER_SIZE).unpack(HEADER_FORMAT)))
|
140
|
+
end
|
141
|
+
|
142
|
+
def send_value(body)
|
143
|
+
bytesize = body.bytesize
|
144
|
+
|
145
|
+
fail Error, 'message to big' if bytesize > MAX_BYTES
|
146
|
+
|
147
|
+
io.binmode
|
148
|
+
io.write([bytesize].pack(HEADER_FORMAT))
|
149
|
+
io.write(body)
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def read(bytes)
|
155
|
+
io.binmode
|
156
|
+
io.read(bytes) or fail Error, 'Unexpected EOF'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def receive_value
|
161
|
+
marshal.load(reader.receive_value)
|
162
|
+
end
|
163
|
+
|
164
|
+
def send_value(value)
|
165
|
+
writer.send_value(marshal.dump(value))
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.from_pipes(marshal:, reader:, writer:)
|
170
|
+
new(
|
171
|
+
marshal: marshal,
|
172
|
+
reader: Frame.new(io: reader.to_reader),
|
173
|
+
writer: Frame.new(io: writer.to_writer)
|
174
|
+
)
|
175
|
+
end
|
176
|
+
end # Connection
|
177
|
+
end # Parallel
|
178
|
+
end # Mutant
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module Parallel
|
5
|
+
class Pipe
|
6
|
+
include Adamantium, Anima.new(:reader, :writer)
|
7
|
+
|
8
|
+
# Run block with pipe in binmode
|
9
|
+
#
|
10
|
+
# @return [undefined]
|
11
|
+
def self.with(io)
|
12
|
+
io.pipe(binmode: true) do |(reader, writer)|
|
13
|
+
yield new(reader: reader, writer: writer)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_io(io)
|
18
|
+
reader, writer = io.pipe(binmode: true)
|
19
|
+
new(reader: reader, writer: writer)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Writer end of the pipe
|
23
|
+
#
|
24
|
+
# @return [IO]
|
25
|
+
def to_writer
|
26
|
+
reader.close
|
27
|
+
writer
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parent reader end of the pipe
|
31
|
+
#
|
32
|
+
# @return [IO]
|
33
|
+
def to_reader
|
34
|
+
writer.close
|
35
|
+
reader
|
36
|
+
end
|
37
|
+
end # Pipe
|
38
|
+
end # Parallel
|
39
|
+
end # Mutant
|
@@ -9,6 +9,7 @@ module Mutant
|
|
9
9
|
:index,
|
10
10
|
:on_process_start,
|
11
11
|
:process_name,
|
12
|
+
:timeout,
|
12
13
|
:var_active_jobs,
|
13
14
|
:var_final,
|
14
15
|
:var_running,
|
@@ -18,38 +19,49 @@ module Mutant
|
|
18
19
|
)
|
19
20
|
end
|
20
21
|
|
21
|
-
include Adamantium, Anima.new(:connection, :
|
22
|
+
include Adamantium, Anima.new(:config, :connection, :log_reader, :pid, :response_reader)
|
22
23
|
|
23
24
|
def self.start(**attributes)
|
24
25
|
start_config(Config.new(**attributes))
|
25
26
|
end
|
26
27
|
|
27
28
|
# rubocop:disable Metrics/MethodLength
|
29
|
+
# rubocop:disable Metrics/AbcSize
|
28
30
|
def self.start_config(config)
|
29
31
|
world = config.world
|
30
32
|
io = world.io
|
31
33
|
marshal = world.marshal
|
32
34
|
|
33
|
-
request
|
34
|
-
response = Pipe.from_io(io)
|
35
|
+
log, request, response = Pipe.from_io(io), Pipe.from_io(io), Pipe.from_io(io)
|
35
36
|
|
36
37
|
pid = world.process.fork do
|
38
|
+
log_writer = log.to_writer
|
39
|
+
|
40
|
+
world.stderr.reopen(log_writer)
|
41
|
+
world.stdout.reopen(log_writer)
|
42
|
+
|
37
43
|
run_child(
|
38
44
|
config: config,
|
39
|
-
connection:
|
45
|
+
connection: Connection.from_pipes(marshal: marshal, reader: request, writer: response),
|
46
|
+
log_writer: log_writer
|
40
47
|
)
|
41
48
|
end
|
42
49
|
|
50
|
+
connection = Connection.from_pipes(marshal: marshal, reader: response, writer: request)
|
51
|
+
|
43
52
|
new(
|
44
|
-
|
45
|
-
|
46
|
-
|
53
|
+
config: config,
|
54
|
+
connection: connection,
|
55
|
+
log_reader: log.to_reader,
|
56
|
+
response_reader: connection.reader.io,
|
57
|
+
pid: pid
|
47
58
|
)
|
48
59
|
end
|
49
60
|
private_class_method :start_config
|
61
|
+
# rubocop:enable Metrics/AbcSize
|
50
62
|
# rubocop:enable Metrics/MethodLength
|
51
63
|
|
52
|
-
def self.run_child(config:, connection:)
|
64
|
+
def self.run_child(config:, connection:, log_writer:)
|
53
65
|
world = config.world
|
54
66
|
|
55
67
|
world.thread.current.name = config.process_name
|
@@ -58,7 +70,9 @@ module Mutant
|
|
58
70
|
config.on_process_start.call(index: config.index)
|
59
71
|
|
60
72
|
loop do
|
61
|
-
|
73
|
+
value = config.block.call(connection.receive_value)
|
74
|
+
log_writer.flush
|
75
|
+
connection.send_value(value)
|
62
76
|
end
|
63
77
|
end
|
64
78
|
private_class_method :run_child
|
@@ -67,26 +81,40 @@ module Mutant
|
|
67
81
|
config.index
|
68
82
|
end
|
69
83
|
|
70
|
-
# Run worker
|
84
|
+
# Run worker loop
|
71
85
|
#
|
72
86
|
# @return [self]
|
87
|
+
#
|
88
|
+
# rubocop:disable Metrics/MethodLength
|
89
|
+
# rubocop:disable Metrics/AbcSize
|
73
90
|
def call
|
74
91
|
loop do
|
75
92
|
job = next_job or break
|
76
93
|
|
77
94
|
job_start(job)
|
78
95
|
|
79
|
-
|
96
|
+
connection.send_value(job.payload)
|
97
|
+
|
98
|
+
response = Connection::Reader.read_response(
|
99
|
+
deadline: config.world.deadline(config.timeout),
|
100
|
+
io: config.world.io,
|
101
|
+
job: job,
|
102
|
+
log_reader: log_reader,
|
103
|
+
marshal: config.world.marshal,
|
104
|
+
response_reader: response_reader
|
105
|
+
)
|
80
106
|
|
81
107
|
job_done(job)
|
82
108
|
|
83
|
-
break if
|
109
|
+
break if add_response(response) || response.error
|
84
110
|
end
|
85
111
|
|
86
112
|
finalize
|
87
113
|
|
88
114
|
self
|
89
115
|
end
|
116
|
+
# rubocop:enable Metrics/AbcSize
|
117
|
+
# rubocop:enable Metrics/MethodLength
|
90
118
|
|
91
119
|
def signal
|
92
120
|
process.kill('TERM', pid)
|
@@ -110,9 +138,9 @@ module Mutant
|
|
110
138
|
end
|
111
139
|
end
|
112
140
|
|
113
|
-
def
|
141
|
+
def add_response(response)
|
114
142
|
config.var_sink.with do |sink|
|
115
|
-
sink.
|
143
|
+
sink.response(response)
|
116
144
|
sink.stop?
|
117
145
|
end
|
118
146
|
end
|
data/lib/mutant/parallel.rb
CHANGED
@@ -9,8 +9,11 @@ module Mutant
|
|
9
9
|
# @param [Config] config
|
10
10
|
#
|
11
11
|
# @return [Driver]
|
12
|
-
def self.async(world
|
13
|
-
shared
|
12
|
+
def self.async(config:, world:)
|
13
|
+
shared = shared_state(world, config)
|
14
|
+
|
15
|
+
world.process_warmup
|
16
|
+
|
14
17
|
workers = workers(world, config, shared)
|
15
18
|
|
16
19
|
Driver.new(
|
@@ -20,6 +23,7 @@ module Mutant
|
|
20
23
|
)
|
21
24
|
end
|
22
25
|
|
26
|
+
# rubocop:disable Metric/MethodLength
|
23
27
|
def self.workers(world, config, shared)
|
24
28
|
Array.new(config.jobs) do |index|
|
25
29
|
Worker.start(
|
@@ -27,12 +31,14 @@ module Mutant
|
|
27
31
|
index: index,
|
28
32
|
on_process_start: config.on_process_start,
|
29
33
|
process_name: "#{config.process_name}-#{index}",
|
34
|
+
timeout: config.timeout,
|
30
35
|
world: world,
|
31
36
|
**shared
|
32
37
|
)
|
33
38
|
end
|
34
39
|
end
|
35
40
|
private_class_method :workers
|
41
|
+
# rubocop:enable Metric/MethodLength
|
36
42
|
|
37
43
|
def self.shared_state(world, config)
|
38
44
|
{
|
@@ -66,16 +72,16 @@ module Mutant
|
|
66
72
|
end
|
67
73
|
private_class_method :shared
|
68
74
|
|
69
|
-
# Job result sink
|
70
|
-
|
75
|
+
# Job result sink signature
|
76
|
+
module Sink
|
71
77
|
include AbstractType
|
72
78
|
|
73
79
|
# Process job result
|
74
80
|
#
|
75
|
-
# @param [
|
81
|
+
# @param [Response]
|
76
82
|
#
|
77
83
|
# @return [self]
|
78
|
-
abstract_method :
|
84
|
+
abstract_method :response
|
79
85
|
|
80
86
|
# The sink status
|
81
87
|
#
|
@@ -97,10 +103,15 @@ module Mutant
|
|
97
103
|
:process_name,
|
98
104
|
:sink,
|
99
105
|
:source,
|
100
|
-
:thread_name
|
106
|
+
:thread_name,
|
107
|
+
:timeout
|
101
108
|
)
|
102
109
|
end # Config
|
103
110
|
|
111
|
+
class Response
|
112
|
+
include Anima.new(:error, :job, :log, :result)
|
113
|
+
end
|
114
|
+
|
104
115
|
# Parallel execution status
|
105
116
|
class Status
|
106
117
|
include Adamantium, Anima.new(
|
@@ -18,11 +18,14 @@ module Mutant
|
|
18
18
|
|
19
19
|
# Progress representation
|
20
20
|
#
|
21
|
-
# @param [Runner::Status] status
|
22
|
-
#
|
23
21
|
# @return [String]
|
24
22
|
abstract_method :progress
|
25
23
|
|
24
|
+
# Progress representation
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
abstract_method :test_progress
|
28
|
+
|
26
29
|
# Report delay in seconds
|
27
30
|
#
|
28
31
|
# @return [Float]
|
@@ -69,6 +72,13 @@ module Mutant
|
|
69
72
|
format(Printer::Env, env)
|
70
73
|
end
|
71
74
|
|
75
|
+
# Test start representation
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
def test_start(env)
|
79
|
+
format(Printer::Test::Env, env)
|
80
|
+
end
|
81
|
+
|
72
82
|
# Progress representation
|
73
83
|
#
|
74
84
|
# @return [String]
|
@@ -76,6 +86,13 @@ module Mutant
|
|
76
86
|
format(Printer::StatusProgressive, status)
|
77
87
|
end
|
78
88
|
|
89
|
+
# Progress representation
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
def test_progress(status)
|
93
|
+
format(Printer::Test::StatusProgressive, status)
|
94
|
+
end
|
95
|
+
|
79
96
|
private
|
80
97
|
|
81
98
|
def new_buffer
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Reporter
|
5
|
+
class CLI
|
6
|
+
class Printer
|
7
|
+
class Test < self
|
8
|
+
# Printer for test config
|
9
|
+
class Config < self
|
10
|
+
|
11
|
+
# Report configuration
|
12
|
+
#
|
13
|
+
# @param [Mutant::Config] config
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
def run
|
18
|
+
info('Fail-Fast: %s', object.fail_fast)
|
19
|
+
info('Integration: %s', object.integration.name || 'null')
|
20
|
+
info('Jobs: %s', object.jobs || 'auto')
|
21
|
+
end
|
22
|
+
end # Config
|
23
|
+
|
24
|
+
# Env printer
|
25
|
+
class Env < self
|
26
|
+
delegate(
|
27
|
+
:amount_available_tests,
|
28
|
+
:amount_selected_tests,
|
29
|
+
:amount_all_tests,
|
30
|
+
:config
|
31
|
+
)
|
32
|
+
|
33
|
+
FORMATS = [
|
34
|
+
].each(&:freeze)
|
35
|
+
|
36
|
+
# Run printer
|
37
|
+
#
|
38
|
+
# @return [undefined]
|
39
|
+
def run
|
40
|
+
info('Test environment:')
|
41
|
+
visit(Config, config)
|
42
|
+
info('Tests: %s', amount_all_tests)
|
43
|
+
end
|
44
|
+
end # Env
|
45
|
+
|
46
|
+
# Full env result reporter
|
47
|
+
class EnvResult < self
|
48
|
+
delegate(
|
49
|
+
:amount_test_results,
|
50
|
+
:amount_tests_failed,
|
51
|
+
:amount_tests_success,
|
52
|
+
:runtime,
|
53
|
+
:testtime
|
54
|
+
)
|
55
|
+
|
56
|
+
FORMATS = [
|
57
|
+
[:info, 'Test-Results: %0d', :amount_test_results ],
|
58
|
+
[:info, 'Test-Failed: %0d', :amount_tests_failed ],
|
59
|
+
[:info, 'Test-Success: %0d', :amount_tests_success ],
|
60
|
+
[:info, 'Runtime: %0.2fs', :runtime ],
|
61
|
+
[:info, 'Testtime: %0.2fs', :testtime ],
|
62
|
+
[:info, 'Efficiency: %0.2f%%', :efficiency_percent ]
|
63
|
+
].each(&:freeze)
|
64
|
+
|
65
|
+
private_constant(*constants(false))
|
66
|
+
|
67
|
+
# Run printer
|
68
|
+
#
|
69
|
+
# @return [undefined]
|
70
|
+
def run
|
71
|
+
visit_collection(Result, object.failed_test_results)
|
72
|
+
visit(Env, object.env)
|
73
|
+
FORMATS.each do |report, format, value|
|
74
|
+
__send__(report, format, __send__(value))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def efficiency_percent
|
81
|
+
(testtime / runtime) * 100
|
82
|
+
end
|
83
|
+
end # EnvResult
|
84
|
+
|
85
|
+
# Reporter for test results
|
86
|
+
class Result < self
|
87
|
+
|
88
|
+
# Run report printer
|
89
|
+
#
|
90
|
+
# @return [undefined]
|
91
|
+
def run
|
92
|
+
puts(object.output)
|
93
|
+
end
|
94
|
+
|
95
|
+
end # Result
|
96
|
+
|
97
|
+
# Reporter for progressive output format on scheduler Status objects
|
98
|
+
class StatusProgressive < self
|
99
|
+
FORMAT = 'progress: %02d/%02d failed: %d runtime: %0.02fs testtime: %0.02fs tests/s: %0.02f'
|
100
|
+
|
101
|
+
delegate(
|
102
|
+
:amount_test_results,
|
103
|
+
:amount_tests,
|
104
|
+
:amount_tests_failed,
|
105
|
+
:testtime,
|
106
|
+
:runtime
|
107
|
+
)
|
108
|
+
|
109
|
+
# Run printer
|
110
|
+
#
|
111
|
+
# @return [undefined]
|
112
|
+
def run
|
113
|
+
status(
|
114
|
+
FORMAT,
|
115
|
+
amount_test_results,
|
116
|
+
amount_tests,
|
117
|
+
amount_tests_failed,
|
118
|
+
runtime,
|
119
|
+
testtime,
|
120
|
+
tests_per_second
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def object
|
127
|
+
super().payload
|
128
|
+
end
|
129
|
+
|
130
|
+
def tests_per_second
|
131
|
+
amount_test_results / runtime
|
132
|
+
end
|
133
|
+
end # StatusProgressive
|
134
|
+
end # Test
|
135
|
+
end # Printer
|
136
|
+
end # CLI
|
137
|
+
end # Reporter
|
138
|
+
end # Mutant
|