mutant 0.15.0 → 0.16.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/VERSION +1 -1
- data/bin/mutant +0 -7
- data/lib/mutant/cli/command/root.rb +1 -1
- data/lib/mutant/cli/command/session.rb +281 -0
- data/lib/mutant/cli/command.rb +16 -2
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/expression/method.rb +0 -2
- data/lib/mutant/expression/methods.rb +0 -2
- data/lib/mutant/expression/namespace.rb +0 -2
- data/lib/mutant/isolation/fork.rb +2 -6
- data/lib/mutant/isolation.rb +31 -0
- data/lib/mutant/matcher/null.rb +1 -1
- data/lib/mutant/mutation/runner/sink.rb +23 -10
- data/lib/mutant/mutation/runner.rb +1 -0
- data/lib/mutant/mutation.rb +3 -20
- data/lib/mutant/mutator/node/literal/integer.rb +61 -0
- data/lib/mutant/parallel/connection.rb +0 -2
- data/lib/mutant/parallel/driver.rb +0 -2
- data/lib/mutant/reporter/cli/printer/alive_results.rb +27 -0
- data/lib/mutant/reporter/cli/printer/env_result.rb +53 -1
- data/lib/mutant/reporter/cli/printer/subject_result.rb +103 -5
- data/lib/mutant/reporter/cli/printer.rb +24 -1
- data/lib/mutant/repository/diff.rb +1 -2
- data/lib/mutant/result/exception.rb +29 -0
- data/lib/mutant/result/json_writer.rb +43 -0
- data/lib/mutant/result/process_status.rb +37 -0
- data/lib/mutant/result/session.rb +63 -0
- data/lib/mutant/result/test.rb +30 -0
- data/lib/mutant/result.rb +201 -96
- data/lib/mutant/segment/recorder.rb +0 -2
- data/lib/mutant/timer.rb +3 -1
- data/lib/mutant/transform/json.rb +68 -0
- data/lib/mutant/transform.rb +33 -25
- data/lib/mutant/version.rb +8 -14
- data/lib/mutant/world.rb +6 -0
- data/lib/mutant/zombifier.rb +0 -2
- data/lib/mutant.rb +13 -4
- metadata +33 -7
- data/lib/mutant/reporter/cli/printer/coverage_result.rb +0 -19
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +0 -84
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Reporter
|
|
5
|
+
class CLI
|
|
6
|
+
class Printer
|
|
7
|
+
# Shared logic for printing alive mutation results
|
|
8
|
+
module AliveResults
|
|
9
|
+
ALIVE_EXPLANATION = <<~'MESSAGE'
|
|
10
|
+
Uncovered mutations detected, exiting nonzero!
|
|
11
|
+
Alive mutations require one of two actions:
|
|
12
|
+
A) Keep the mutated code: Your tests specify the correct semantics,
|
|
13
|
+
and the original code is redundant. Accept the mutation.
|
|
14
|
+
B) Add a missing test: The original code is correct, but the tests
|
|
15
|
+
do not verify the behavior the mutation removed.
|
|
16
|
+
MESSAGE
|
|
17
|
+
|
|
18
|
+
def print_alive_results(failed_subject_results)
|
|
19
|
+
failed_subject_results.each do |subject_result|
|
|
20
|
+
SubjectResult.call(display_config:, output:, object: subject_result)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end # AliveResults
|
|
24
|
+
end # Printer
|
|
25
|
+
end # CLI
|
|
26
|
+
end # Reporter
|
|
27
|
+
end # Mutant
|
|
@@ -8,13 +8,65 @@ module Mutant
|
|
|
8
8
|
class EnvResult < self
|
|
9
9
|
delegate(:failed_subject_results)
|
|
10
10
|
|
|
11
|
+
SEPARATOR = '-----------------------'
|
|
12
|
+
MORE_MESSAGE = '(%d more alive mutation(s), use `mutant session subject %s` to see all details)'
|
|
13
|
+
STATS_FORMAT = 'tests: %d, runtime: %.2fs, killtime: %.2fs'
|
|
14
|
+
NO_DIFF_MESSAGE = 'BUG: No diff generated, please report circumstances to https://github.com/mbj/mutant'
|
|
15
|
+
|
|
16
|
+
private_constant(*constants(false))
|
|
17
|
+
|
|
11
18
|
# Run printer
|
|
12
19
|
#
|
|
13
20
|
# @return [undefined]
|
|
14
21
|
def run
|
|
15
|
-
|
|
22
|
+
unless failed_subject_results.empty?
|
|
23
|
+
puts(AliveResults::ALIVE_EXPLANATION)
|
|
24
|
+
failed_subject_results.each(&method(:print_subject_summary))
|
|
25
|
+
end
|
|
16
26
|
visit(EnvProgress, object)
|
|
17
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def print_subject_summary(subject_result)
|
|
32
|
+
uncovered = subject_result.uncovered_results
|
|
33
|
+
|
|
34
|
+
if uncovered.any? { |coverage_result| critical?(coverage_result.mutation_result) }
|
|
35
|
+
SubjectResult.call(output:, object: subject_result)
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
print_subject_line(subject_result)
|
|
40
|
+
print_mutation_diff(uncovered.first.mutation_result)
|
|
41
|
+
|
|
42
|
+
remaining = uncovered.length - 1
|
|
43
|
+
|
|
44
|
+
return unless remaining.positive?
|
|
45
|
+
|
|
46
|
+
puts(MORE_MESSAGE % [remaining, subject_result.expression_syntax])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def print_subject_line(subject_result)
|
|
50
|
+
status(subject_result.identification)
|
|
51
|
+
puts(STATS_FORMAT % [subject_result.tests.length, subject_result.runtime, subject_result.killtime])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def critical?(mutation_result)
|
|
55
|
+
!mutation_result.mutation_type.eql?('evil')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def print_mutation_diff(mutation_result)
|
|
59
|
+
puts(mutation_result.mutation_identification)
|
|
60
|
+
puts(SEPARATOR)
|
|
61
|
+
diff = mutation_result.mutation_diff
|
|
62
|
+
|
|
63
|
+
if diff
|
|
64
|
+
output.write(color? ? colorize_diff(diff) : diff)
|
|
65
|
+
else
|
|
66
|
+
puts(NO_DIFF_MESSAGE)
|
|
67
|
+
end
|
|
68
|
+
puts(SEPARATOR)
|
|
69
|
+
end
|
|
18
70
|
end # EnvResult
|
|
19
71
|
end # Printer
|
|
20
72
|
end # CLI
|
|
@@ -5,19 +5,117 @@ module Mutant
|
|
|
5
5
|
class CLI
|
|
6
6
|
class Printer
|
|
7
7
|
# Subject result printer
|
|
8
|
+
#
|
|
9
|
+
# Renders subject identification, tests, and all uncovered mutations
|
|
10
|
+
# inline so that subject context (source, node) is available when
|
|
11
|
+
# printing mutation details.
|
|
8
12
|
class SubjectResult < self
|
|
9
13
|
|
|
10
|
-
delegate :
|
|
14
|
+
delegate :uncovered_results, :tests
|
|
15
|
+
|
|
16
|
+
MAP = {
|
|
17
|
+
'evil' => :evil_details,
|
|
18
|
+
'neutral' => :neutral_details,
|
|
19
|
+
'noop' => :noop_details
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
NEUTRAL_MESSAGE = <<~'MESSAGE'
|
|
23
|
+
--- Neutral failure ---
|
|
24
|
+
Original code was inserted unmutated. And the test did NOT PASS.
|
|
25
|
+
Your tests do not pass initially or you found a bug in mutant / unparser.
|
|
26
|
+
Subject AST:
|
|
27
|
+
%s
|
|
28
|
+
Unparsed Source:
|
|
29
|
+
%s
|
|
30
|
+
MESSAGE
|
|
31
|
+
|
|
32
|
+
NO_DIFF_MESSAGE = <<~'MESSAGE'
|
|
33
|
+
--- Internal failure ---
|
|
34
|
+
BUG: A generated mutation did not result in exactly one diff hunk!
|
|
35
|
+
This is an invariant violation by the mutation generation engine.
|
|
36
|
+
Please report a reproduction to https://github.com/mbj/mutant
|
|
37
|
+
Original unparsed source:
|
|
38
|
+
%s
|
|
39
|
+
Original AST:
|
|
40
|
+
%s
|
|
41
|
+
Mutated unparsed source:
|
|
42
|
+
%s
|
|
43
|
+
Mutated AST:
|
|
44
|
+
%s
|
|
45
|
+
MESSAGE
|
|
46
|
+
|
|
47
|
+
NOOP_MESSAGE = <<~'MESSAGE'
|
|
48
|
+
---- Noop failure -----
|
|
49
|
+
No code was inserted. And the test did NOT PASS.
|
|
50
|
+
This is typically a problem of your specs not passing unmutated.
|
|
51
|
+
MESSAGE
|
|
52
|
+
|
|
53
|
+
SEPARATOR = '-----------------------'
|
|
54
|
+
STATS_FORMAT = 'tests: %d, runtime: %.2fs, killtime: %.2fs'
|
|
55
|
+
|
|
56
|
+
private_constant(*constants(false))
|
|
11
57
|
|
|
12
58
|
# Run report printer
|
|
13
59
|
#
|
|
14
60
|
# @return [undefined]
|
|
15
61
|
def run
|
|
16
|
-
status(
|
|
17
|
-
tests.
|
|
18
|
-
|
|
62
|
+
status(object.identification)
|
|
63
|
+
puts(STATS_FORMAT % [tests.length, object.runtime, object.killtime])
|
|
64
|
+
uncovered_results.each do |coverage_result|
|
|
65
|
+
print_mutation_result(coverage_result.mutation_result)
|
|
66
|
+
end
|
|
67
|
+
print_selected_tests
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def print_selected_tests
|
|
73
|
+
if tests.empty?
|
|
74
|
+
puts('no selected tests')
|
|
75
|
+
else
|
|
76
|
+
puts("selected tests (#{tests.length}):")
|
|
77
|
+
tests.each do |test|
|
|
78
|
+
puts("- #{test.identification}")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def print_mutation_result(mutation_result)
|
|
84
|
+
puts(mutation_result.mutation_identification)
|
|
85
|
+
puts(SEPARATOR)
|
|
86
|
+
visit(IsolationResult, mutation_result.isolation_result) if show_isolation_logs?(mutation_result)
|
|
87
|
+
__send__(MAP.fetch(mutation_result.mutation_type), mutation_result)
|
|
88
|
+
puts(SEPARATOR)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def show_isolation_logs?(mutation_result)
|
|
92
|
+
display_config.isolation_logs || !mutation_result.mutation_type.eql?('evil')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# rubocop:disable Metrics/MethodLength
|
|
96
|
+
def evil_details(mutation_result)
|
|
97
|
+
diff = mutation_result.mutation_diff
|
|
98
|
+
|
|
99
|
+
if diff
|
|
100
|
+
output.write(color? ? colorize_diff(diff) : diff)
|
|
101
|
+
else
|
|
102
|
+
info(
|
|
103
|
+
NO_DIFF_MESSAGE,
|
|
104
|
+
object.source,
|
|
105
|
+
object.node.inspect,
|
|
106
|
+
mutation_result.mutation_source,
|
|
107
|
+
mutation_result.mutation_node.inspect
|
|
108
|
+
)
|
|
19
109
|
end
|
|
20
|
-
|
|
110
|
+
end
|
|
111
|
+
# rubocop:enable Metrics/MethodLength
|
|
112
|
+
|
|
113
|
+
def noop_details(_mutation_result)
|
|
114
|
+
info(NOOP_MESSAGE)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def neutral_details(mutation_result)
|
|
118
|
+
info(NEUTRAL_MESSAGE, object.node.inspect, mutation_result.mutation_source)
|
|
21
119
|
end
|
|
22
120
|
|
|
23
121
|
end # SubjectResult
|
|
@@ -5,13 +5,25 @@ module Mutant
|
|
|
5
5
|
class CLI
|
|
6
6
|
# CLI runner status printer base class
|
|
7
7
|
class Printer
|
|
8
|
+
# Printer display options
|
|
9
|
+
class DisplayConfig
|
|
10
|
+
include Anima.new(:isolation_logs)
|
|
11
|
+
|
|
12
|
+
DEFAULT = new(isolation_logs: false)
|
|
13
|
+
VERBOSE = new(isolation_logs: true)
|
|
14
|
+
end
|
|
15
|
+
|
|
8
16
|
include(
|
|
9
17
|
AbstractType,
|
|
10
18
|
Adamantium,
|
|
11
|
-
Anima.new(:output, :object),
|
|
19
|
+
Anima.new(:display_config, :output, :object),
|
|
12
20
|
Procto
|
|
13
21
|
)
|
|
14
22
|
|
|
23
|
+
def self.call(output:, object:, display_config: DisplayConfig::DEFAULT)
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
15
27
|
private_class_method :new
|
|
16
28
|
|
|
17
29
|
def call = run
|
|
@@ -82,6 +94,17 @@ module Mutant
|
|
|
82
94
|
end
|
|
83
95
|
|
|
84
96
|
alias_method :color?, :tty?
|
|
97
|
+
|
|
98
|
+
def colorize_diff(raw_diff)
|
|
99
|
+
raw_diff.lines.map do |line|
|
|
100
|
+
case line[0]
|
|
101
|
+
when '+' then Unparser::Color::GREEN.format(line)
|
|
102
|
+
when '-' then Unparser::Color::RED.format(line)
|
|
103
|
+
else
|
|
104
|
+
line
|
|
105
|
+
end
|
|
106
|
+
end.join
|
|
107
|
+
end
|
|
85
108
|
end # Printer
|
|
86
109
|
end # CLI
|
|
87
110
|
end # Reporter
|
|
@@ -60,14 +60,13 @@ module Mutant
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# rubocop:disable Metrics/MethodLength
|
|
63
|
-
# mutant:disable (3.2 specific mutation)
|
|
64
63
|
def parse_line(root, line)
|
|
65
64
|
match = FORMAT.match(line)
|
|
66
65
|
|
|
67
66
|
if match
|
|
68
67
|
Either::Right.new(
|
|
69
68
|
Path.new(
|
|
70
|
-
path: root.join(Util.one(match
|
|
69
|
+
path: root.join(Util.one(match)),
|
|
71
70
|
to:,
|
|
72
71
|
world:
|
|
73
72
|
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
module Result
|
|
5
|
+
# Serializable exception data
|
|
6
|
+
class Exception
|
|
7
|
+
include Anima.new(
|
|
8
|
+
:backtrace,
|
|
9
|
+
:message,
|
|
10
|
+
:original_class
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Build from a Ruby exception
|
|
14
|
+
#
|
|
15
|
+
# @param [::Exception] exception
|
|
16
|
+
#
|
|
17
|
+
# @return [Exception]
|
|
18
|
+
def self.from_exception(exception)
|
|
19
|
+
new(
|
|
20
|
+
backtrace: exception.backtrace,
|
|
21
|
+
message: exception.message,
|
|
22
|
+
original_class: exception.class.name
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
JSON = Transform::JSON.for_anima(self)
|
|
27
|
+
end # Exception
|
|
28
|
+
end # Result
|
|
29
|
+
end # Mutant
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
module Result
|
|
5
|
+
# Write result JSON to .mutant/results/
|
|
6
|
+
class JSONWriter
|
|
7
|
+
include Anima.new(:env, :result)
|
|
8
|
+
|
|
9
|
+
RESULTS_DIR = '.mutant/results'
|
|
10
|
+
|
|
11
|
+
# Write result JSON file
|
|
12
|
+
#
|
|
13
|
+
# @return [Pathname]
|
|
14
|
+
def call
|
|
15
|
+
dir = env.world.pathname.new(RESULTS_DIR)
|
|
16
|
+
dir.mkpath
|
|
17
|
+
|
|
18
|
+
path = dir.join("#{SESSION_ID}.json")
|
|
19
|
+
path.write(json)
|
|
20
|
+
|
|
21
|
+
path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def json
|
|
27
|
+
JSON.generate(Session::JSON.dump(session).from_right)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def session
|
|
31
|
+
Session.new(
|
|
32
|
+
killtime: result.killtime,
|
|
33
|
+
mutant_version: VERSION,
|
|
34
|
+
pid: env.world.process.pid,
|
|
35
|
+
ruby_version: RUBY_VERSION,
|
|
36
|
+
runtime: result.runtime,
|
|
37
|
+
session_id: SESSION_ID,
|
|
38
|
+
subject_results: result.subject_results
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end # JSONWriter
|
|
42
|
+
end # Result
|
|
43
|
+
end # Mutant
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
module Result
|
|
5
|
+
# Serializable process status
|
|
6
|
+
#
|
|
7
|
+
# Replaces Process::Status in the result tree with a
|
|
8
|
+
# round-trippable value object.
|
|
9
|
+
class ProcessStatus
|
|
10
|
+
include Anima.new(:exitstatus)
|
|
11
|
+
|
|
12
|
+
# Build from a Process::Status object
|
|
13
|
+
#
|
|
14
|
+
# @param [Process::Status] process_status
|
|
15
|
+
#
|
|
16
|
+
# @return [ProcessStatus]
|
|
17
|
+
def self.from_process_status(process_status)
|
|
18
|
+
new(exitstatus: process_status.exitstatus)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Stable inspect without memory address for use in user-facing output
|
|
22
|
+
#
|
|
23
|
+
# @return [String]
|
|
24
|
+
def inspect
|
|
25
|
+
"#<#{self.class.name} exitstatus=#{exitstatus}>"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Test for successful exit
|
|
29
|
+
#
|
|
30
|
+
# @return [Boolean]
|
|
31
|
+
def success?
|
|
32
|
+
exitstatus.equal?(0)
|
|
33
|
+
end
|
|
34
|
+
JSON = Transform::JSON.for_anima(self)
|
|
35
|
+
end # ProcessStatus
|
|
36
|
+
end # Result
|
|
37
|
+
end # Mutant
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
module Result
|
|
5
|
+
# Top-level result object containing session metadata and subject results
|
|
6
|
+
class Session
|
|
7
|
+
# Extract timestamp from UUIDv7 session_id
|
|
8
|
+
module Timestamp
|
|
9
|
+
# @return [Time]
|
|
10
|
+
def timestamp
|
|
11
|
+
ms = session_id.delete('-')[0, 12].to_i(16)
|
|
12
|
+
Time.at(ms / 1000.0).utc
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
include Timestamp, Anima.new(
|
|
17
|
+
:killtime,
|
|
18
|
+
:mutant_version,
|
|
19
|
+
:pid,
|
|
20
|
+
:ruby_version,
|
|
21
|
+
:runtime,
|
|
22
|
+
:session_id,
|
|
23
|
+
:subject_results
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
dump = Transform::Success.new(
|
|
27
|
+
block: lambda do |object|
|
|
28
|
+
{
|
|
29
|
+
'killtime' => object.killtime,
|
|
30
|
+
'mutant_version' => object.mutant_version,
|
|
31
|
+
'pid' => object.pid,
|
|
32
|
+
'ruby_version' => object.ruby_version,
|
|
33
|
+
'runtime' => object.runtime,
|
|
34
|
+
'session_id' => object.session_id,
|
|
35
|
+
'subject_results' => object.subject_results.map { |subject_result| Subject::JSON.dump(subject_result).from_right }
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
load = Transform::Sequence.new(
|
|
41
|
+
steps: [
|
|
42
|
+
Transform::Hash.new(
|
|
43
|
+
required: [
|
|
44
|
+
Transform::Hash::Key.new(value: 'killtime', transform: Transform::FLOAT),
|
|
45
|
+
Transform::Hash::Key.new(value: 'mutant_version', transform: Transform::STRING),
|
|
46
|
+
Transform::Hash::Key.new(value: 'pid', transform: Transform::INTEGER),
|
|
47
|
+
Transform::Hash::Key.new(value: 'ruby_version', transform: Transform::STRING),
|
|
48
|
+
Transform::Hash::Key.new(value: 'runtime', transform: Transform::FLOAT),
|
|
49
|
+
Transform::Hash::Key.new(value: 'session_id', transform: Transform::STRING),
|
|
50
|
+
Transform::Hash::Key.new(value: 'subject_results', transform: Transform::Array.new(transform: Subject::JSON.load_transform))
|
|
51
|
+
],
|
|
52
|
+
optional: []
|
|
53
|
+
),
|
|
54
|
+
Transform::Hash::Symbolize.new,
|
|
55
|
+
Transform::Success.new(block: method(:new).to_proc)
|
|
56
|
+
]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
JSON = Transform::JSON.new(dump_transform: dump, load_transform: load)
|
|
60
|
+
|
|
61
|
+
end # Session
|
|
62
|
+
end # Result
|
|
63
|
+
end # Mutant
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
module Result
|
|
5
|
+
# Test result
|
|
6
|
+
class Test
|
|
7
|
+
include Anima.new(:job_index, :passed, :runtime, :output)
|
|
8
|
+
|
|
9
|
+
alias_method :success?, :passed
|
|
10
|
+
|
|
11
|
+
class VoidValue < self
|
|
12
|
+
include Singleton
|
|
13
|
+
|
|
14
|
+
# Initialize object
|
|
15
|
+
#
|
|
16
|
+
# @return [undefined]
|
|
17
|
+
def initialize
|
|
18
|
+
super(
|
|
19
|
+
job_index: nil,
|
|
20
|
+
output: '',
|
|
21
|
+
passed: false,
|
|
22
|
+
runtime: 0.0
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
end # VoidValue
|
|
26
|
+
|
|
27
|
+
JSON = Transform::JSON.for_anima(self)
|
|
28
|
+
end # Test
|
|
29
|
+
end # Result
|
|
30
|
+
end # Mutant
|