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