henitai 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 586e777c1eb78b5345eedde4f010ab262fe1ced6e0ac3714e21dbd9aa7cc63d3
4
- data.tar.gz: 7d6d5d3939777b28ab127133dc4f2d9663c29c812f6f4a77af4705a3f36ef973
3
+ metadata.gz: d11d8d5361258fac7bddc006b8fd8485d1d085c3eaf294afc5dc97c6eed9ee7c
4
+ data.tar.gz: c3620c79cd2b1e59ec43abb143775e0b5edcbc2ed89b3384caa39703e7b9759a
5
5
  SHA512:
6
- metadata.gz: 0fd445f2506b0dec762413e3e992af6b93797728c4253a86de2e1ce0f60fcd189ac2d2f342033ac8d16653064ce3fc3cd885c69ad34026e9781c60ca88d85de4
7
- data.tar.gz: 9d00b7d9f3237b70460306aae62729d38e0142fbf7b954c2b4528feda3a4e596f08bd373c9ed448fd0ff12de7c5c1fb867c3ea9f8cf8582792a9c3a454d2bfd5
6
+ metadata.gz: eed2ea62f262d95b79bae130a0d19437749c07b8873d532bd029755a72d9600335131167da004fe01dcb9d83427fb6155de1d841df4ea8dac0e2b4651de33d26
7
+ data.tar.gz: a4709f5c4804af79ea9599a62dfce714487c6d947bc8f85459cebebe206c4f0f7df965bff8c0aedacca227dceb67b32876733e9bfb450dc96357e2ef879f07c4
data/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ 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
+
18
+ ## [0.1.1] - 2026-04-03
19
+
20
+ ### Added
21
+ - Minitest integration for Rails projects: injects SimpleCov for coverage
22
+ collection, sets `RAILS_ENV=test` and `PARALLEL_WORKERS=1` in the baseline
23
+ subprocess, preloads `config/environment.rb` before mutant activation, adds
24
+ `test/` to `$LOAD_PATH` before forking, and excludes `test/system/` by default
25
+ - `simplecov` runtime dependency (required by the Minitest integration)
26
+
27
+ ### Fixed
28
+ - `rspec/core` was unconditionally required at load time, causing a `LoadError`
29
+ in projects that do not have RSpec installed — now loaded lazily only when the
30
+ RSpec integration is used
31
+ - Coverage path normalisation now uses `File.realpath` so symlinked temp
32
+ directories on macOS no longer cause false no-coverage results
33
+
34
+ ## [0.1.0] - 2026-03-01
35
+
10
36
  ### Added
11
37
  - Initial gem scaffold with Ruby 4.0.2 support
12
38
  - Dev Container configuration (official `ruby:4.0.2-alpine` base image, Codex CLI preinstalled)
@@ -16,4 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
42
  - CLI critical path: `henitai run` now executes the full pipeline, supports `--since`, returns CI-friendly exit codes, and `henitai version` prints `Henitai::VERSION`
17
43
  - RSpec per-test coverage output: `henitai/coverage_formatter` now writes `coverage/henitai_per_test.json`
18
44
 
19
- [Unreleased]: https://github.com/martinotten/henitai/commits/main
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
47
+ [0.1.1]: https://github.com/martinotten/henitai/compare/v0.1.0...v0.1.1
48
+ [0.1.0]: https://github.com/martinotten/henitai/releases/tag/v0.1.0
@@ -9,7 +9,6 @@ module Henitai
9
9
 
10
10
  def ensure!(source_files:, config:, integration:)
11
11
  return if source_files.empty?
12
- return if coverage_available?(source_files, config)
13
12
 
14
13
  bootstrap_coverage(integration, config)
15
14
  return if coverage_available?(source_files, config)
@@ -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.transform_values { |lines| lines.uniq.sort }
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
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "fileutils"
4
4
  require "minitest"
5
- require "rspec/core"
6
5
 
7
6
  module Henitai
8
7
  # Namespace for test-framework integrations.
@@ -57,8 +56,8 @@ module Henitai
57
56
 
58
57
  def build_child_output_files(log_paths)
59
58
  {
60
- original_stdout: $stdout.dup,
61
- original_stderr: $stderr.dup,
59
+ original_stdout: stdout_stream.dup,
60
+ original_stderr: stderr_stream.dup,
62
61
  stdout_file: File.new(log_paths[:stdout_path], "w"),
63
62
  stderr_file: File.new(log_paths[:stderr_path], "w")
64
63
  }
@@ -70,13 +69,17 @@ module Henitai
70
69
  end
71
70
 
72
71
  def redirect_child_output(output_files)
73
- $stdout.reopen(output_files[:stdout_file])
74
- $stderr.reopen(output_files[:stderr_file])
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
75
76
  end
76
77
 
77
78
  def restore_child_output(output_files)
78
- reopen_child_output_stream($stdout, output_files[:original_stdout])
79
- reopen_child_output_stream($stderr, output_files[:original_stderr])
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]
80
83
  end
81
84
 
82
85
  def reopen_child_output_stream(stream, original_stream)
@@ -95,6 +98,14 @@ module Henitai
95
98
  reports_dir = ENV.fetch("HENITAI_REPORTS_DIR", "reports")
96
99
  File.join(reports_dir, "mutation-coverage", mutant_id.to_s)
97
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
98
109
  end
99
110
 
100
111
  # Integration adapter for RSpec.
@@ -106,7 +117,12 @@ module Henitai
106
117
  def self.for(name)
107
118
  const_get(name.capitalize)
108
119
  rescue NameError
109
- raise ArgumentError, "Unknown integration: #{name}. Available: rspec"
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}"
110
126
  end
111
127
 
112
128
  # Base class for all integrations.
@@ -131,6 +147,12 @@ module Henitai
131
147
  def run_mutant(mutant:, test_files:, timeout:)
132
148
  raise NotImplementedError
133
149
  end
150
+
151
+ private
152
+
153
+ def pause(seconds)
154
+ sleep(seconds)
155
+ end
134
156
  end
135
157
 
136
158
  # RSpec integration adapter.
@@ -186,6 +208,7 @@ module Henitai
186
208
  private
187
209
 
188
210
  def run_in_child(mutant:, test_files:, log_paths:)
211
+ Thread.report_on_exception = false
189
212
  scenario_log_support.with_coverage_dir(mutant.id) do
190
213
  scenario_log_support.capture_child_output(log_paths) do
191
214
  return 2 if Mutant::Activator.activate!(mutant) == :compile_error
@@ -224,6 +247,7 @@ module Henitai
224
247
  end
225
248
 
226
249
  def run_tests(test_files)
250
+ require "rspec/core"
227
251
  status = RSpec::Core::Runner.run(test_files + rspec_options)
228
252
  return status if status.is_a?(Integer)
229
253
 
@@ -231,11 +255,7 @@ module Henitai
231
255
  end
232
256
 
233
257
  def rspec_options
234
- ["--require", "henitai/coverage_formatter"]
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
@@ -391,10 +411,33 @@ module Henitai
391
411
  # runner. Minitest shares selection and execution semantics, but per-test
392
412
  # coverage collection is not yet wired into this path.
393
413
  class Minitest < Rspec
414
+ def run_mutant(mutant:, test_files:, timeout:)
415
+ setup_load_path
416
+ super
417
+ end
418
+
419
+ def run_in_child(mutant:, test_files:, log_paths:)
420
+ ENV["RAILS_ENV"] = "test" unless ENV["RAILS_ENV"] == "test"
421
+ preload_environment
422
+ super
423
+ end
424
+
425
+ def run_suite(test_files, timeout: DEFAULT_SUITE_TIMEOUT)
426
+ log_paths = scenario_log_paths("baseline")
427
+ FileUtils.mkdir_p(File.dirname(log_paths[:stdout_path]))
428
+ pid = File.open(log_paths[:stdout_path], "w") do |stdout_file|
429
+ File.open(log_paths[:stderr_path], "w") do |stderr_file|
430
+ Process.spawn(subprocess_env, *suite_command(test_files), out: stdout_file, err: stderr_file)
431
+ end
432
+ end
433
+ build_result(wait_with_timeout(pid, timeout), log_paths)
434
+ end
435
+
394
436
  private
395
437
 
396
438
  def suite_command(test_files)
397
439
  ["bundle", "exec", "ruby", "-I", "test",
440
+ "-r", "henitai/minitest_simplecov",
398
441
  "-e", "ARGV.each { |f| require File.expand_path(f) }",
399
442
  *test_files]
400
443
  end
@@ -409,8 +452,26 @@ module Henitai
409
452
  status == true ? 0 : 1
410
453
  end
411
454
 
455
+ def preload_environment
456
+ env_file = File.expand_path("config/environment.rb")
457
+ require env_file if File.exist?(env_file)
458
+ end
459
+
460
+ def setup_load_path
461
+ test_dir = File.expand_path("test")
462
+ $LOAD_PATH.unshift(test_dir) unless $LOAD_PATH.include?(test_dir)
463
+ end
464
+
465
+ def subprocess_env
466
+ env = {} # : Hash[String, String]
467
+ env["RAILS_ENV"] = "test" unless ENV["RAILS_ENV"] == "test"
468
+ env["PARALLEL_WORKERS"] = "1"
469
+ env
470
+ end
471
+
412
472
  def spec_files
413
- Dir.glob("test/**/*_test.rb") + Dir.glob("test/**/*_spec.rb")
473
+ (Dir.glob("test/**/*_test.rb") + Dir.glob("test/**/*_spec.rb"))
474
+ .reject { |f| f.start_with?("test/system/") }
414
475
  end
415
476
  end
416
477
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Injected by henitai into the Minitest baseline subprocess to collect
4
+ # line coverage and write it as a SimpleCov-compatible .resultset.json.
5
+ #
6
+ # Must be required before any application code is loaded so that Coverage
7
+ # tracking is active from the first line.
8
+
9
+ require "coverage"
10
+ Coverage.start(lines: true, branches: true, methods: true)
11
+
12
+ require "simplecov"
13
+
14
+ SimpleCov.coverage_dir(ENV.fetch("HENITAI_COVERAGE_DIR", "coverage"))
15
+ SimpleCov.start
@@ -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
- target_for(subject).class_eval(method_source(mutant), __FILE__, __LINE__ + 1)
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
- mutated_subject = replace_node(
61
- subject_node,
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
- updated_children = node.children.map do |child|
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 same_node?(left, right)
83
- left_location = node_location_signature(left)
84
- right_location = node_location_signature(right)
85
- return left.equal?(right) unless left_location && right_location
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
- left_location == right_location
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 replace_child(child, original_node, mutated_node)
91
- case child
92
- when Parser::AST::Node
93
- replace_node(child, original_node, mutated_node)
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
- child
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 node_location_signature(node)
215
- expression = node&.location&.expression
216
- return unless expression
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)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core"
4
+ require "henitai/coverage_formatter"
5
+
6
+ RSpec::Core::Formatters.register(
7
+ Henitai::CoverageFormatter,
8
+ :example_finished,
9
+ :dump_summary
10
+ )
@@ -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
- start_line = mutant.location[:start_line]
101
-
102
- Array(coverage_lines[file]).include?(start_line)
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?
@@ -147,7 +183,10 @@ module Henitai
147
183
  end
148
184
 
149
185
  def normalize_path(path)
150
- File.expand_path(path)
186
+ expanded = File.expand_path(path)
187
+ File.realpath(expanded)
188
+ rescue Errno::ENOENT, Errno::ENOTDIR
189
+ expanded
151
190
  end
152
191
 
153
192
  def equivalence_detector
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Henitai
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
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]
@@ -282,9 +287,17 @@ module Henitai
282
287
  end
283
288
 
284
289
  class Minitest < Rspec
290
+ def run_mutant: (mutant: Mutant, test_files: Array[String], timeout: Float) -> ScenarioExecutionResult
291
+ def run_suite: (Array[String], ?timeout: Float) -> ScenarioExecutionResult
292
+
285
293
  private
286
294
 
295
+ def run_in_child: (mutant: Mutant, test_files: Array[String], log_paths: Hash[Symbol, String]) -> Integer
296
+ def suite_command: (Array[String]) -> Array[String]
287
297
  def run_tests: (Array[String]) -> Integer
298
+ def preload_environment: () -> void
299
+ def setup_load_path: () -> void
300
+ def subprocess_env: () -> Hash[String, String]
288
301
  def spec_files: () -> Array[String]
289
302
  end
290
303
  end
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.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Otten
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.5'
26
+ - !ruby/object:Gem::Dependency
27
+ name: simplecov
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.22'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.22'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: sqlite3
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -83,6 +97,7 @@ files:
83
97
  - lib/henitai/execution_engine.rb
84
98
  - lib/henitai/git_diff_analyzer.rb
85
99
  - lib/henitai/integration.rb
100
+ - lib/henitai/minitest_simplecov.rb
86
101
  - lib/henitai/mutant.rb
87
102
  - lib/henitai/mutant/activator.rb
88
103
  - lib/henitai/mutant_generator.rb
@@ -107,6 +122,7 @@ files:
107
122
  - lib/henitai/parser_current.rb
108
123
  - lib/henitai/reporter.rb
109
124
  - lib/henitai/result.rb
125
+ - lib/henitai/rspec_coverage_formatter.rb
110
126
  - lib/henitai/runner.rb
111
127
  - lib/henitai/sampling_strategy.rb
112
128
  - lib/henitai/scenario_execution_result.rb
@@ -131,7 +147,7 @@ metadata:
131
147
  changelog_uri: https://github.com/martinotten/henitai/blob/main/CHANGELOG.md
132
148
  documentation_uri: https://github.com/martinotten/henitai/blob/main/README.md
133
149
  homepage_uri: https://github.com/martinotten/henitai
134
- source_code_uri: https://github.com/martinotten/henitai/tree/v0.1.0
150
+ source_code_uri: https://github.com/martinotten/henitai/tree/v0.1.2
135
151
  rubygems_mfa_required: 'true'
136
152
  rdoc_options: []
137
153
  require_paths: