henitai 0.1.1 → 0.1.2
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/CHANGELOG.md +10 -1
- data/lib/henitai/coverage_bootstrapper.rb +0 -1
- data/lib/henitai/coverage_formatter.rb +4 -4
- data/lib/henitai/integration.rb +32 -12
- data/lib/henitai/minitest_simplecov.rb +3 -0
- data/lib/henitai/mutant/activator.rb +78 -42
- data/lib/henitai/rspec_coverage_formatter.rb +10 -0
- data/lib/henitai/static_filter.rb +39 -3
- data/lib/henitai/version.rb +1 -1
- data/sig/henitai.rbs +6 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d11d8d5361258fac7bddc006b8fd8485d1d085c3eaf294afc5dc97c6eed9ee7c
|
|
4
|
+
data.tar.gz: c3620c79cd2b1e59ec43abb143775e0b5edcbc2ed89b3384caa39703e7b9759a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eed2ea62f262d95b79bae130a0d19437749c07b8873d532bd029755a72d9600335131167da004fe01dcb9d83427fb6155de1d841df4ea8dac0e2b4651de33d26
|
|
7
|
+
data.tar.gz: a4709f5c4804af79ea9599a62dfce714487c6d947bc8f85459cebebe206c4f0f7df965bff8c0aedacca227dceb67b32876733e9bfb450dc96357e2ef879f07c4
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.2] - 2026-04-07
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `Henitai::Mutant::Activator` now rewrites heredoc-backed method bodies from
|
|
14
|
+
source slices instead of unparsing the whole body, eliminating timeouts on
|
|
15
|
+
HTML reporter mutants
|
|
16
|
+
- `henitai run -v` now stops before the run pipeline starts
|
|
17
|
+
|
|
10
18
|
## [0.1.1] - 2026-04-03
|
|
11
19
|
|
|
12
20
|
### Added
|
|
@@ -34,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
34
42
|
- CLI critical path: `henitai run` now executes the full pipeline, supports `--since`, returns CI-friendly exit codes, and `henitai version` prints `Henitai::VERSION`
|
|
35
43
|
- RSpec per-test coverage output: `henitai/coverage_formatter` now writes `coverage/henitai_per_test.json`
|
|
36
44
|
|
|
37
|
-
[Unreleased]: https://github.com/martinotten/henitai/compare/v0.1.
|
|
45
|
+
[Unreleased]: https://github.com/martinotten/henitai/compare/v0.1.2...HEAD
|
|
46
|
+
[0.1.2]: https://github.com/martinotten/henitai/compare/v0.1.1...v0.1.2
|
|
38
47
|
[0.1.1]: https://github.com/martinotten/henitai/compare/v0.1.0...v0.1.1
|
|
39
48
|
[0.1.0]: https://github.com/martinotten/henitai/releases/tag/v0.1.0
|
|
@@ -10,8 +10,6 @@ module Henitai
|
|
|
10
10
|
REPORT_DIR_ENV = "HENITAI_REPORTS_DIR"
|
|
11
11
|
REPORT_FILE_NAME = "henitai_per_test.json"
|
|
12
12
|
|
|
13
|
-
RSpec::Core::Formatters.register self, :example_finished, :dump_summary
|
|
14
|
-
|
|
15
13
|
def initialize(_output)
|
|
16
14
|
@coverage_by_test = Hash.new do |hash, test_file|
|
|
17
15
|
hash[test_file] = Hash.new { |nested, source_file| nested[source_file] = [] }
|
|
@@ -91,7 +89,7 @@ module Henitai
|
|
|
91
89
|
def line_counts_for(file_coverage)
|
|
92
90
|
case file_coverage
|
|
93
91
|
when Hash
|
|
94
|
-
Array(file_coverage["lines"])
|
|
92
|
+
Array(file_coverage[:lines] || file_coverage["lines"])
|
|
95
93
|
else
|
|
96
94
|
Array(file_coverage)
|
|
97
95
|
end
|
|
@@ -105,7 +103,9 @@ module Henitai
|
|
|
105
103
|
|
|
106
104
|
def serializable_report
|
|
107
105
|
@coverage_by_test.transform_values do |source_map|
|
|
108
|
-
source_map.
|
|
106
|
+
source_map.to_h do |source_file, lines|
|
|
107
|
+
[File.expand_path(source_file), lines.uniq.sort]
|
|
108
|
+
end
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
end
|
data/lib/henitai/integration.rb
CHANGED
|
@@ -56,8 +56,8 @@ module Henitai
|
|
|
56
56
|
|
|
57
57
|
def build_child_output_files(log_paths)
|
|
58
58
|
{
|
|
59
|
-
original_stdout:
|
|
60
|
-
original_stderr:
|
|
59
|
+
original_stdout: stdout_stream.dup,
|
|
60
|
+
original_stderr: stderr_stream.dup,
|
|
61
61
|
stdout_file: File.new(log_paths[:stdout_path], "w"),
|
|
62
62
|
stderr_file: File.new(log_paths[:stderr_path], "w")
|
|
63
63
|
}
|
|
@@ -69,13 +69,17 @@ module Henitai
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def redirect_child_output(output_files)
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
reopen_child_output_stream(stdout_stream, output_files[:stdout_file])
|
|
73
|
+
reopen_child_output_stream(stderr_stream, output_files[:stderr_file])
|
|
74
|
+
$stdout = stdout_stream
|
|
75
|
+
$stderr = stderr_stream
|
|
74
76
|
end
|
|
75
77
|
|
|
76
78
|
def restore_child_output(output_files)
|
|
77
|
-
reopen_child_output_stream(
|
|
78
|
-
reopen_child_output_stream(
|
|
79
|
+
reopen_child_output_stream(stdout_stream, output_files[:original_stdout])
|
|
80
|
+
reopen_child_output_stream(stderr_stream, output_files[:original_stderr])
|
|
81
|
+
$stdout = output_files[:original_stdout]
|
|
82
|
+
$stderr = output_files[:original_stderr]
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
def reopen_child_output_stream(stream, original_stream)
|
|
@@ -94,6 +98,14 @@ module Henitai
|
|
|
94
98
|
reports_dir = ENV.fetch("HENITAI_REPORTS_DIR", "reports")
|
|
95
99
|
File.join(reports_dir, "mutation-coverage", mutant_id.to_s)
|
|
96
100
|
end
|
|
101
|
+
|
|
102
|
+
def stdout_stream
|
|
103
|
+
@stdout_stream ||= IO.for_fd(1)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def stderr_stream
|
|
107
|
+
@stderr_stream ||= IO.for_fd(2)
|
|
108
|
+
end
|
|
97
109
|
end
|
|
98
110
|
|
|
99
111
|
# Integration adapter for RSpec.
|
|
@@ -105,7 +117,12 @@ module Henitai
|
|
|
105
117
|
def self.for(name)
|
|
106
118
|
const_get(name.capitalize)
|
|
107
119
|
rescue NameError
|
|
108
|
-
|
|
120
|
+
available = constants.filter_map do |constant_name|
|
|
121
|
+
integration = const_get(constant_name)
|
|
122
|
+
constant_name.to_s.downcase if integration.is_a?(Class) && integration < Base
|
|
123
|
+
end.sort.join(", ")
|
|
124
|
+
|
|
125
|
+
raise ArgumentError, "Unknown integration: #{name}. Available: #{available}"
|
|
109
126
|
end
|
|
110
127
|
|
|
111
128
|
# Base class for all integrations.
|
|
@@ -130,6 +147,12 @@ module Henitai
|
|
|
130
147
|
def run_mutant(mutant:, test_files:, timeout:)
|
|
131
148
|
raise NotImplementedError
|
|
132
149
|
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def pause(seconds)
|
|
154
|
+
sleep(seconds)
|
|
155
|
+
end
|
|
133
156
|
end
|
|
134
157
|
|
|
135
158
|
# RSpec integration adapter.
|
|
@@ -185,6 +208,7 @@ module Henitai
|
|
|
185
208
|
private
|
|
186
209
|
|
|
187
210
|
def run_in_child(mutant:, test_files:, log_paths:)
|
|
211
|
+
Thread.report_on_exception = false
|
|
188
212
|
scenario_log_support.with_coverage_dir(mutant.id) do
|
|
189
213
|
scenario_log_support.capture_child_output(log_paths) do
|
|
190
214
|
return 2 if Mutant::Activator.activate!(mutant) == :compile_error
|
|
@@ -231,11 +255,7 @@ module Henitai
|
|
|
231
255
|
end
|
|
232
256
|
|
|
233
257
|
def rspec_options
|
|
234
|
-
["--require", "henitai/
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def pause(seconds)
|
|
238
|
-
sleep(seconds)
|
|
258
|
+
["--require", "henitai/rspec_coverage_formatter"]
|
|
239
259
|
end
|
|
240
260
|
|
|
241
261
|
def scenario_log_support
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
# Must be required before any application code is loaded so that Coverage
|
|
7
7
|
# tracking is active from the first line.
|
|
8
8
|
|
|
9
|
+
require "coverage"
|
|
10
|
+
Coverage.start(lines: true, branches: true, methods: true)
|
|
11
|
+
|
|
9
12
|
require "simplecov"
|
|
10
13
|
|
|
11
14
|
SimpleCov.coverage_dir(ENV.fetch("HENITAI_COVERAGE_DIR", "coverage"))
|
|
@@ -7,6 +7,22 @@ module Henitai
|
|
|
7
7
|
class Mutant
|
|
8
8
|
# Activates a mutant inside the forked child process.
|
|
9
9
|
class Activator
|
|
10
|
+
# Filters "already initialized constant" C-level warnings that fire when
|
|
11
|
+
# a source file is loaded into a process that already has the constant
|
|
12
|
+
# defined via require. Uses a thread-local flag so the filter is active
|
|
13
|
+
# only during load_source_file, leaving all other warnings untouched.
|
|
14
|
+
module ConstantRedefinitionFilter
|
|
15
|
+
PATTERN = /already initialized constant|previous definition of/
|
|
16
|
+
private_constant :PATTERN
|
|
17
|
+
|
|
18
|
+
def warn(msg, **kwargs)
|
|
19
|
+
return if Thread.current[:henitai_filter_const_warnings] && PATTERN.match?(msg.to_s)
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
Warning.singleton_class.prepend(ConstantRedefinitionFilter)
|
|
25
|
+
|
|
10
26
|
SERIALIZER_METHODS = {
|
|
11
27
|
arg: :argument_parameter_fragment,
|
|
12
28
|
optarg: :optional_parameter_fragment,
|
|
@@ -26,8 +42,9 @@ module Henitai
|
|
|
26
42
|
subject = mutant.subject
|
|
27
43
|
raise ArgumentError, "Cannot activate wildcard subjects" if subject.method_name.nil?
|
|
28
44
|
|
|
45
|
+
target = target_for(subject)
|
|
29
46
|
Henitai::WarningSilencer.silence do
|
|
30
|
-
|
|
47
|
+
target.class_eval(method_source(mutant), __FILE__, __LINE__ + 1)
|
|
31
48
|
nil
|
|
32
49
|
end
|
|
33
50
|
rescue Unparser::UnsupportedNodeError
|
|
@@ -57,44 +74,28 @@ module Henitai
|
|
|
57
74
|
subject_node = mutant.subject.ast_node
|
|
58
75
|
return compile_safe_unparse(mutant.mutated_node) unless subject_node
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
mutant.original_node,
|
|
63
|
-
mutant.mutated_node
|
|
64
|
-
)
|
|
65
|
-
body = method_body(mutated_subject) || Parser::AST::Node.new(:nil, [])
|
|
66
|
-
compile_safe_unparse(body)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def replace_node(node, original_node, mutated_node)
|
|
70
|
-
return mutated_node if same_node?(node, original_node)
|
|
71
|
-
return node unless node.is_a?(Parser::AST::Node)
|
|
77
|
+
body = method_body(subject_node)
|
|
78
|
+
return compile_safe_unparse(Parser::AST::Node.new(:nil, [])) unless body
|
|
72
79
|
|
|
73
|
-
|
|
74
|
-
replace_child(child, original_node, mutated_node)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
return node if updated_children == node.children
|
|
78
|
-
|
|
79
|
-
Parser::AST::Node.new(node.type, updated_children)
|
|
80
|
+
body_source_for_mutant(body, mutant)
|
|
80
81
|
end
|
|
81
82
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return
|
|
83
|
+
def body_source_for_mutant(body, mutant)
|
|
84
|
+
original_range = mutant.original_node.location&.expression
|
|
85
|
+
location = body.location
|
|
86
|
+
return source_body(location, body) unless original_range && location
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
replacement = compile_safe_unparse(mutant.mutated_node)
|
|
89
|
+
body_source_for_location(location, original_range, replacement, body)
|
|
88
90
|
end
|
|
89
91
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
when Array
|
|
95
|
-
child.map { |item| replace_child(item, original_node, mutated_node) }
|
|
92
|
+
def body_source_for_location(location, original_range, replacement, body)
|
|
93
|
+
if heredoc_location?(location)
|
|
94
|
+
heredoc_body_source(location, original_range, replacement) ||
|
|
95
|
+
source_body(location, body)
|
|
96
96
|
else
|
|
97
|
-
|
|
97
|
+
expression_source(location, original_range, replacement) ||
|
|
98
|
+
source_body(location, body)
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
|
|
@@ -184,6 +185,38 @@ module Henitai
|
|
|
184
185
|
subject_node.children[1]
|
|
185
186
|
end
|
|
186
187
|
|
|
188
|
+
def heredoc_location?(location)
|
|
189
|
+
location.respond_to?(:heredoc_body) && location.heredoc_body
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def heredoc_body_source(location, original_range, replacement)
|
|
193
|
+
body_source = replace_source_fragment(
|
|
194
|
+
location.heredoc_body,
|
|
195
|
+
original_range,
|
|
196
|
+
replacement
|
|
197
|
+
)
|
|
198
|
+
return unless body_source
|
|
199
|
+
|
|
200
|
+
"#{location.expression.source}\n#{body_source}#{location.heredoc_end.source}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def source_body(location, body)
|
|
204
|
+
return compile_safe_unparse(body) unless location
|
|
205
|
+
|
|
206
|
+
if heredoc_location?(location)
|
|
207
|
+
"#{location.expression.source}\n#{location.heredoc_body.source}#{location.heredoc_end.source}"
|
|
208
|
+
else
|
|
209
|
+
location.expression.source
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def expression_source(location, original_range, replacement)
|
|
214
|
+
source_range = location.expression
|
|
215
|
+
return unless source_range
|
|
216
|
+
|
|
217
|
+
replace_source_fragment(source_range, original_range, replacement)
|
|
218
|
+
end
|
|
219
|
+
|
|
187
220
|
def load_target(subject)
|
|
188
221
|
Object.const_get(subject.namespace.delete_prefix("::"))
|
|
189
222
|
rescue NameError
|
|
@@ -195,7 +228,10 @@ module Henitai
|
|
|
195
228
|
source_file = subject.source_file || source_file_from_ast(subject)
|
|
196
229
|
return unless source_file && File.file?(source_file)
|
|
197
230
|
|
|
231
|
+
Thread.current[:henitai_filter_const_warnings] = true
|
|
198
232
|
load(source_file)
|
|
233
|
+
ensure
|
|
234
|
+
Thread.current[:henitai_filter_const_warnings] = false
|
|
199
235
|
end
|
|
200
236
|
|
|
201
237
|
def source_file_from_ast(subject)
|
|
@@ -211,17 +247,17 @@ module Henitai
|
|
|
211
247
|
expression.source_buffer.name
|
|
212
248
|
end
|
|
213
249
|
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
|
|
250
|
+
def replace_source_fragment(source_range, original_range, replacement)
|
|
251
|
+
source = source_range.source
|
|
252
|
+
start = original_range.begin_pos - source_range.begin_pos
|
|
253
|
+
stop = original_range.end_pos - source_range.begin_pos
|
|
254
|
+
return unless start >= 0 && stop <= source.bytesize && start <= stop
|
|
255
|
+
|
|
256
|
+
prefix = source.byteslice(0, start)
|
|
257
|
+
suffix = source.byteslice(stop, source.bytesize - stop)
|
|
258
|
+
return unless prefix && suffix
|
|
217
259
|
|
|
218
|
-
|
|
219
|
-
expression.source_buffer.name,
|
|
220
|
-
expression.line,
|
|
221
|
-
expression.column,
|
|
222
|
-
expression.last_line,
|
|
223
|
-
expression.last_column
|
|
224
|
-
]
|
|
260
|
+
prefix + replacement + suffix
|
|
225
261
|
end
|
|
226
262
|
|
|
227
263
|
def compile_safe_unparse(node)
|
|
@@ -32,6 +32,7 @@ module Henitai
|
|
|
32
32
|
per_test_coverage_report_path = per_test_coverage_report_path(config)
|
|
33
33
|
|
|
34
34
|
coverage_lines = coverage_lines_by_file(coverage_report_path)
|
|
35
|
+
coverage_lines = merge_method_coverage(coverage_lines, coverage_report_path)
|
|
35
36
|
return coverage_lines unless coverage_lines.empty?
|
|
36
37
|
|
|
37
38
|
coverage_lines_from_test_lines(
|
|
@@ -97,9 +98,10 @@ module Henitai
|
|
|
97
98
|
|
|
98
99
|
def covered?(mutant, coverage_lines)
|
|
99
100
|
file = normalize_path(mutant.location[:file])
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
covered = Array(coverage_lines[file])
|
|
102
|
+
(mutant.location[:start_line]..mutant.location[:end_line]).any? do |line|
|
|
103
|
+
covered.include?(line)
|
|
104
|
+
end
|
|
103
105
|
end
|
|
104
106
|
|
|
105
107
|
def source_for(mutant)
|
|
@@ -115,6 +117,40 @@ module Henitai
|
|
|
115
117
|
@compiled_ignore_patterns[patterns] ||= patterns.map { |pattern| Regexp.new(pattern) }
|
|
116
118
|
end
|
|
117
119
|
|
|
120
|
+
def merge_method_coverage(coverage_lines, path)
|
|
121
|
+
return coverage_lines unless File.exist?(path)
|
|
122
|
+
|
|
123
|
+
JSON.parse(File.read(path)).each_value do |suite|
|
|
124
|
+
suite.fetch("coverage", {}).each do |file, file_coverage|
|
|
125
|
+
merge_file_method_coverage(coverage_lines, file, file_coverage)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
coverage_lines.transform_values(&:sort)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def merge_file_method_coverage(coverage_lines, file, file_coverage)
|
|
133
|
+
methods = file_coverage["methods"]
|
|
134
|
+
return unless methods.is_a?(Hash)
|
|
135
|
+
|
|
136
|
+
normalized = normalize_path(file)
|
|
137
|
+
methods.each do |key, count|
|
|
138
|
+
next unless count.to_i.positive?
|
|
139
|
+
|
|
140
|
+
range = method_line_range(key)
|
|
141
|
+
next unless range
|
|
142
|
+
|
|
143
|
+
coverage_lines[normalized] = Array(coverage_lines[normalized]) | range.to_a
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def method_line_range(key)
|
|
148
|
+
m = key.match(/(\d+), \d+, (\d+), \d+\]\z/)
|
|
149
|
+
return unless m
|
|
150
|
+
|
|
151
|
+
(m.captures.first.to_i..m.captures.last.to_i)
|
|
152
|
+
end
|
|
153
|
+
|
|
118
154
|
def covered_lines(file_coverage)
|
|
119
155
|
Array(file_coverage["lines"]).each_with_index.filter_map do |count, index|
|
|
120
156
|
index + 1 if count.to_i.positive?
|
data/lib/henitai/version.rb
CHANGED
data/sig/henitai.rbs
CHANGED
|
@@ -223,6 +223,10 @@ module Henitai
|
|
|
223
223
|
def test_files: () -> Array[String]
|
|
224
224
|
def run_mutant: (mutant: Mutant, test_files: Array[String], timeout: Float) -> ScenarioExecutionResult
|
|
225
225
|
def run_suite: (Array[String], ?timeout: Float) -> ScenarioExecutionResult
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def pause: (Float) -> void
|
|
226
230
|
end
|
|
227
231
|
|
|
228
232
|
class ScenarioLogSupport
|
|
@@ -240,6 +244,8 @@ module Henitai
|
|
|
240
244
|
def reopen_child_output_stream: (untyped, untyped) -> void
|
|
241
245
|
def close_child_output_files: (Hash[Symbol, untyped]) -> void
|
|
242
246
|
def mutation_coverage_dir: (String) -> String
|
|
247
|
+
def stdout_stream: () -> IO
|
|
248
|
+
def stderr_stream: () -> IO
|
|
243
249
|
end
|
|
244
250
|
|
|
245
251
|
class Rspec < Base
|
|
@@ -265,7 +271,6 @@ module Henitai
|
|
|
265
271
|
def combined_log: (String, String) -> String
|
|
266
272
|
def scenario_log_paths: (String) -> Hash[Symbol, String]
|
|
267
273
|
def run_tests: (Array[String]) -> Integer
|
|
268
|
-
def pause: (Float) -> void
|
|
269
274
|
def scenario_log_support: () -> ScenarioLogSupport
|
|
270
275
|
def rspec_options: () -> Array[String]
|
|
271
276
|
def spec_files: () -> Array[String]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: henitai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Martin Otten
|
|
@@ -122,6 +122,7 @@ files:
|
|
|
122
122
|
- lib/henitai/parser_current.rb
|
|
123
123
|
- lib/henitai/reporter.rb
|
|
124
124
|
- lib/henitai/result.rb
|
|
125
|
+
- lib/henitai/rspec_coverage_formatter.rb
|
|
125
126
|
- lib/henitai/runner.rb
|
|
126
127
|
- lib/henitai/sampling_strategy.rb
|
|
127
128
|
- lib/henitai/scenario_execution_result.rb
|
|
@@ -146,7 +147,7 @@ metadata:
|
|
|
146
147
|
changelog_uri: https://github.com/martinotten/henitai/blob/main/CHANGELOG.md
|
|
147
148
|
documentation_uri: https://github.com/martinotten/henitai/blob/main/README.md
|
|
148
149
|
homepage_uri: https://github.com/martinotten/henitai
|
|
149
|
-
source_code_uri: https://github.com/martinotten/henitai/tree/v0.1.
|
|
150
|
+
source_code_uri: https://github.com/martinotten/henitai/tree/v0.1.2
|
|
150
151
|
rubygems_mfa_required: 'true'
|
|
151
152
|
rdoc_options: []
|
|
152
153
|
require_paths:
|