henitai 0.5.1 → 0.5.3

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: 321426b14e3d03dae68885665b6abee8e11ea48d7a00036664dd0b6a10af2659
4
- data.tar.gz: 262e708c7393a11e57f8fe25b9588b8b6aa04c026bebef1dec0aa77fedd782ba
3
+ metadata.gz: 0530ecd98105a394b4466cbe23704f848565c55e318c35a31e096eedc2cba299
4
+ data.tar.gz: 56c64c5b679b0bc7c66691a4a7b20c274ac90d1d812d7abb65e778190cd6bb39
5
5
  SHA512:
6
- metadata.gz: 4ef8ed1d823903bceca334c81a895a1734745480e0966ab55e100ea95b8479201824ebbd048ea86f0465c0a98fd39dd561a62876614e20bed7b1f64460d10e20
7
- data.tar.gz: b152279d4854b7aeb55afb5696a77054184947f0ea16570dd9e3f90a62388295b6f4f6499f101d323a8fa0071c9d83d8847e3348c878061c980587ab6ddfd53f
6
+ metadata.gz: e8725233ea8c806ca65fc488560daafbfc2300f723a054039b29dd45625a935217ebdbe609fde3e2df693299ee5ca069d35ce00b63a4e9a4d90dcfa9e77e2920
7
+ data.tar.gz: d8052c091f3967d1cc873cb34b02f04ad339714f08950841f65e7ef1ff064573bb0f54226eb3725d29dd3e7a42727d535b469396c76aa0a0ae2ffb5ae1075336
data/CHANGELOG.md CHANGED
@@ -7,9 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.3] - 2026-08-26
11
+
12
+ ### Fixed
13
+
14
+ - A timed-out Minitest baseline suite is now actually killed. Its child was
15
+ spawned without `pgroup: true`, so the parent's group-wide `SIGTERM` named a
16
+ process group that never existed, raised `ESRCH`, and left the suite running;
17
+ the parent then blocked in `Process.wait` until the suite finished on its own
18
+ — minutes past the timeout it had already reported, and long after the log
19
+ tail quoted in the error had stopped describing reality. The suite child now
20
+ leads its own group, as the RSpec baseline child already did, and post-run
21
+ cleanup signals that group instead of the single pid, so a Rails test server
22
+ or browser driver started by the suite is torn down with it.
23
+
24
+ ## [0.5.2] - 2026-08-26
25
+
26
+ ### Changed
27
+
28
+ - `test_excludes` now also applies to the coverage-bootstrap suite, not only to
29
+ per-mutant test selection. Slow end-to-end or integration suites could be kept
30
+ away from mutant children but still had to run in full during the baseline,
31
+ where the fixed suite timeout aborts the run with
32
+ `Henitai::CoverageError: Configured test suite failed while generating
33
+ coverage` even though the suite itself was green. Excluded files are also
34
+ dropped from the coverage freshness watch list, so a test that no longer
35
+ contributes to the baseline no longer invalidates it. Lines covered only by
36
+ excluded tests now count as uncovered and are not mutated — matching what
37
+ already happened at execution time, where such mutants could never be killed.
38
+ A `test_excludes` set that leaves no test file now aborts with
39
+ `Henitai::ConfigurationError` instead of running an empty suite and then
40
+ reporting missing coverage.
41
+
10
42
  ## [0.5.1] - 2026-08-25
11
43
 
12
44
  ### Changed
45
+
13
46
  - `minitest` runtime dependency widened from `~> 5.25` to `>= 5.25, < 7`.
14
47
  The floor is unchanged; only the ceiling moved. `~> 5.25` capped the
15
48
  range below 6.0, so bundles resolving minitest 6.x could not install
@@ -20,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20
53
  ## [0.5.0] - 2026-08-21
21
54
 
22
55
  ### Security
56
+
23
57
  - `sqlite3` requirement raised from `~> 1.7` to `>= 2.9.5, < 3`, excluding the
24
58
  vulnerable releases up to and including 2.9.4. This is a runtime major bump:
25
59
  applications pinned to `sqlite3` 1.x will need to upgrade before installing
@@ -28,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
62
  floors so a regression fails the suite rather than shipping quietly
29
63
 
30
64
  ### Changed
65
+
31
66
  - `coverage_criteria` now actually affects scoring. The block was documented,
32
67
  defaulted and validated since it was introduced, but nothing ever read it —
33
68
  `Result` hardcoded the `MS` numerator as `killed + timeout + runtime_error`,
@@ -50,6 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
50
85
  4.0-only form), and CI runs the floor alongside 4.0.2
51
86
 
52
87
  ### Fixed
88
+
53
89
  - Forked mutant children no longer outlive a parent that dies without warning.
54
90
  Children run in their own process group, and all parent-side cleanup —
55
91
  timeout kills, graceful drain, signal traps — only runs while the parent's
@@ -84,6 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
84
120
  0.3.1; this restores it
85
121
 
86
122
  ### Internal
123
+
87
124
  - Test-suite coupling to private implementation details is now guarded by
88
125
  `spec/infra/private_method_reach_spec.rb`, a ratchet: every spec that reaches
89
126
  a private method through `send` or an instance-variable poke carries a
@@ -104,6 +141,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
104
141
  ## [0.4.0] - 2026-07-14
105
142
 
106
143
  ### Added
144
+
107
145
  - `hard` operator set (`mutation.operators: hard` / `--operators hard`), a
108
146
  strict superset of `full` for usually-unkillable mutations (ADR-12):
109
147
  currently `EqualityIdentityOperator` and the new `HashKeyType`
@@ -113,6 +151,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
113
151
  single-pair hashes and double-splat entries are skipped
114
152
 
115
153
  ### Changed
154
+
116
155
  - `full` now means "usually killable": the symbol-key -> string-key mutation
117
156
  moved from `HashLiteral` into the new hard-set `HashKeyType`, and
118
157
  `EqualityIdentityOperator` moved from `full` to `hard` — `full` runs emit
@@ -121,6 +160,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
121
160
  ## [0.3.1] - 2026-07-13
122
161
 
123
162
  ### Fixed
163
+
124
164
  - Per-mutant log capture no longer crashes with
125
165
  `Encoding::UndefinedConversionError` when the host app sets
126
166
  `Encoding.default_internal` (as Rails test environments do) and a child
@@ -130,6 +170,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
170
  from corrupting the file.
131
171
 
132
172
  ### Changed
173
+
133
174
  - `--since REF` now includes the working tree: tracked files with
134
175
  uncommitted changes and untracked files count as changed, matching what
135
176
  actually gets tested. Previously only committed changes in `REF..HEAD`
@@ -146,6 +187,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
146
187
  ## [0.3.0] - 2026-07-13
147
188
 
148
189
  ### Added
190
+
149
191
  - `henitai run --incremental`: verdict reuse from the history store. Killed
150
192
  verdicts are reused when the subject's source and every covering test file
151
193
  are byte-identical to what was recorded; Survived verdicts are additionally
@@ -181,6 +223,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
181
223
  moved out of the default light set into its own operator (ADR-10)
182
224
 
183
225
  ### Fixed
226
+
184
227
  - Mutants in `module_function` modules were unkillable: `module_function`
185
228
  copies the method onto the module's singleton and activation only replaced
186
229
  the instance side, so callers kept running original code and every such
@@ -200,6 +243,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
200
243
  - Flaky-retry counter no longer inflates when a respawned child fails again
201
244
 
202
245
  ### Changed
246
+
203
247
  - Terminal reporter renders original nodes from their source slice and reuses
204
248
  a memoized unparse of the mutated node shared with the JSON reporter
205
249
  (a dogfood profile spent 27% of wall time re-unparsing survivors)
@@ -213,6 +257,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
213
257
  redefinition warnings into their logs
214
258
 
215
259
  ### Internal
260
+
216
261
  - Architecture documentation gained §8.11 (incremental verdict reuse) and
217
262
  ADR-11 (content-fingerprint verdict reuse, not git scoping); new backlog
218
263
  tickets for CI warm verdict cache, child null formatter, and auto-`--since`
@@ -224,12 +269,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
224
269
  ## [0.2.1] - 2026-06-25
225
270
 
226
271
  ### Fixed
272
+
227
273
  - `henitai run` now exits 0 when a run evaluates no valid mutants, instead of
228
274
  failing a CI gate on a vacuous result
229
275
  - Flaky-retry counts are now recorded on the parallel execution path (they were
230
276
  always reported as 0 regardless of actual retries)
231
277
 
232
278
  ### Changed
279
+
233
280
  - `Result` and `Reporter::Json` take their IO as an injected dependency,
234
281
  restoring the domain/infrastructure boundary (no public API change)
235
282
  - Decomposed the monolithic `integration.rb` into single-responsibility files
@@ -238,6 +285,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
238
285
  - Narrowed broad rescues in `safe_unparse` and per-test coverage snapshotting
239
286
 
240
287
  ### Internal
288
+
241
289
  - De-mocked runner specs, added `process_wakeup` and helper coverage, and
242
290
  removed sleep- and chdir-based flakiness from the suite
243
291
  - Enabled full-operator dogfooding configuration and isolated the
@@ -247,6 +295,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
247
295
  ## [0.2.0] - 2026-04-30
248
296
 
249
297
  ### Added
298
+
250
299
  - `--survivors-from <path>` flag on `henitai run` for survivor-only reruns:
251
300
  re-execute only the mutants that survived a prior full run without re-running
252
301
  the whole suite
@@ -281,6 +330,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
281
330
  - x86_64 platform added to gem platform list
282
331
 
283
332
  ### Changed
333
+
284
334
  - JSON mutation report vendor extension now always includes `sessionId`
285
335
  (and `gitSha` when available) to support survivor-only reruns
286
336
  - Terminal reporter labels partial reruns as "Partial survivor rerun" and
@@ -295,6 +345,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
295
345
  `max_flaky_retries: 3` added
296
346
 
297
347
  ### Fixed
348
+
298
349
  - Minitest autorun hook suppressed in mutation child processes to prevent
299
350
  spurious re-runs of the full suite inside each fork
300
351
  - Coverage bootstrap RSpec subprocess ARGV leakage resolved — child processes
@@ -307,6 +358,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
307
358
  preventing stale cache hits after edits
308
359
 
309
360
  ### Performance
361
+
310
362
  - File discovery cached in the integration layer; repeated calls within one
311
363
  run no longer re-scan the filesystem
312
364
  - Polling sleep removed from the scheduler hot loop; slots are refilled
@@ -315,6 +367,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
315
367
  ## [0.1.10] - 2026-04-16
316
368
 
317
369
  ### Fixed
370
+
318
371
  - RuboCop `RSpec/MultipleExpectations` offenses in `coverage_formatter_spec` and
319
372
  `per_test_coverage_collector_spec` resolved by splitting each two-assertion
320
373
  example into focused single-expectation examples
@@ -322,6 +375,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
322
375
  ## [0.1.9] - 2026-04-16
323
376
 
324
377
  ### Fixed
378
+
325
379
  - SimpleCov is now suppressed during Minitest mutant child runs: `SimpleCov.start`
326
380
  is turned into a no-op before test files are required, eliminating the
327
381
  "Stopped processing SimpleCov as a previous error has been detected" warning
@@ -330,12 +384,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
330
384
  ## [0.1.8] - 2026-04-16
331
385
 
332
386
  ### Added
387
+
333
388
  - Minitest integration now supports per-test coverage: a `MinitestCoverageReporter`
334
389
  snapshots `Coverage.peek_result` after each test and writes `henitai_per_test.json`,
335
390
  enabling targeted mutation runs that only execute the tests covering the mutated lines —
336
391
  the same efficiency that was previously available only to RSpec projects
337
392
 
338
393
  ### Fixed
394
+
339
395
  - RSpec integration now respects `--exclude-pattern` entries in `.rspec` so excluded
340
396
  spec files are not passed to the runner during mutation runs
341
397
  - Per-test source file filter corrected to check only the project-relative path prefix
@@ -345,11 +401,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
345
401
  ## [0.1.7] - 2026-04-14
346
402
 
347
403
  ### Added
404
+
348
405
  - Committed integration smoke projects for RSpec and Minitest, runnable via
349
406
  `bundle exec rake smoke:integration:all`, to exercise `henitai` against
350
407
  small real projects that depend on the local source checkout
351
408
 
352
409
  ### Fixed
410
+
353
411
  - Child-process stdout/stderr restoration after captured test runs now keeps
354
412
  the standard streams usable, preventing passing mutant executions from being
355
413
  misclassified because the child exited with a closed stdio handle
@@ -360,6 +418,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
360
418
  ## [0.1.6] - 2026-04-14
361
419
 
362
420
  ### Fixed
421
+
363
422
  - Minitest is no longer required eagerly when `henitai/integration` loads, so
364
423
  projects that do not include Minitest in their bundle can still boot and use
365
424
  non-Minitest integrations without a parent-process `LoadError`
@@ -367,6 +426,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
367
426
  ## [0.1.5] - 2026-04-14
368
427
 
369
428
  ### Fixed
429
+
370
430
  - Steep type errors in `Runner` after removing targeted-run bootstrap scoping:
371
431
  updated `bootstrap_mutants` RBS signature to match the new single-argument
372
432
  form and removed stale signatures for `refresh_coverage_for_targeted_run`,
@@ -375,6 +435,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
375
435
  workspace setup and report writing into helper methods
376
436
 
377
437
  ### Changed
438
+
378
439
  - Targeted-run coverage bootstrap no longer scopes the initial run to the
379
440
  subject's test files; the full suite is always used for the baseline,
380
441
  trading a minor performance optimisation for reliability
@@ -382,6 +443,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
382
443
  ## [0.1.4] - 2026-04-14
383
444
 
384
445
  ### Fixed
446
+
385
447
  - `StringLiteral` operator no longer generates no-op mutations where the
386
448
  replacement equals the original value (e.g. the spurious `"" → ""` mutant
387
449
  that was emitted for methods already returning an empty string literal)
@@ -397,6 +459,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
397
459
  runner's configured reports directory
398
460
 
399
461
  ### Changed
462
+
400
463
  - `ScenarioExecutionResult.build` factory method consolidates status and
401
464
  exit-status derivation that was previously spread across `Integration`,
402
465
  reducing the mutation surface of the value object
@@ -404,6 +467,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
404
467
  ## [0.1.3] - 2026-04-13
405
468
 
406
469
  ### Added
470
+
407
471
  - Four new mutation operators: `UnaryOperator` (negates boolean and numeric
408
472
  unary expressions), `UpdateOperator` (swaps `+=`/`-=`/`*=` and targets
409
473
  compound-assignment nodes), `RegexMutator` (replaces regex literals with
@@ -422,6 +486,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
422
486
  `x || false`, `true && x`, `x && true` are suppressed as equivalent mutants
423
487
 
424
488
  ### Changed
489
+
425
490
  - Per-line mutation cap (`max_mutants_per_line`) removed from the generator,
426
491
  configuration schema, and validator — see ADR-08. All syntactically valid
427
492
  mutations on a line are now generated unconditionally
@@ -436,6 +501,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
436
501
  without reflection
437
502
 
438
503
  ### Performance
504
+
439
505
  - Coverage bootstrap freshness check: the baseline RSpec run is skipped when
440
506
  `.resultset.json` is newer than every watched source and test file,
441
507
  eliminating ~83 % of bootstrap wall time on repeated runs within a session
@@ -457,6 +523,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
457
523
  AST node
458
524
 
459
525
  ### Fixed
526
+
460
527
  - Mutant child processes now run in isolated process groups (`setpgid`);
461
528
  `cleanup_process_group` sends `SIGTERM` to the entire group on timeout or
462
529
  error, preventing orphaned subprocesses
@@ -472,10 +539,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
472
539
  ## [0.1.2] - 2026-04-07
473
540
 
474
541
  ### Added
542
+
475
543
  - Method coverage is now enabled in both RSpec and Minitest bootstraps, and
476
544
  the static filter merges method-level coverage into the line map
477
545
 
478
546
  ### Fixed
547
+
479
548
  - Coverage baseline regeneration now happens on every `henitai run`, so stale
480
549
  coverage state does not leak between runs
481
550
  - Coverage handling now accepts symbol-keyed `Coverage.peek_result` output and
@@ -492,6 +561,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
492
561
  ## [0.1.1] - 2026-04-03
493
562
 
494
563
  ### Added
564
+
495
565
  - Minitest integration for Rails projects: injects SimpleCov for coverage
496
566
  collection, sets `RAILS_ENV=test` and `PARALLEL_WORKERS=1` in the baseline
497
567
  subprocess, preloads `config/environment.rb` before mutant activation, adds
@@ -499,6 +569,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
499
569
  - `simplecov` runtime dependency (required by the Minitest integration)
500
570
 
501
571
  ### Fixed
572
+
502
573
  - `rspec/core` was unconditionally required at load time, causing a `LoadError`
503
574
  in projects that do not have RSpec installed — now loaded lazily only when the
504
575
  RSpec integration is used
@@ -508,6 +579,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
508
579
  ## [0.1.0] - 2026-03-01
509
580
 
510
581
  ### Added
582
+
511
583
  - Initial gem scaffold with Ruby 4.0.2 support
512
584
  - Dev Container configuration (official `ruby:4.0.2-alpine` base image, Codex CLI preinstalled)
513
585
  - CI pipeline (RuboCop + RSpec + incremental mutation testing on PRs)
@@ -516,7 +588,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
516
588
  - CLI critical path: `henitai run` now executes the full pipeline, supports `--since`, returns CI-friendly exit codes, and `henitai version` prints `Henitai::VERSION`
517
589
  - RSpec per-test coverage output: `henitai/coverage_formatter` now writes `coverage/henitai_per_test.json`
518
590
 
519
- [Unreleased]: https://github.com/martinotten/henitai/compare/v0.5.1...HEAD
591
+ [Unreleased]: https://github.com/martinotten/henitai/compare/v0.5.3...HEAD
592
+ [0.5.3]: https://github.com/martinotten/henitai/compare/v0.5.2...v0.5.3
593
+ [0.5.2]: https://github.com/martinotten/henitai/compare/v0.5.1...v0.5.2
520
594
  [0.5.1]: https://github.com/martinotten/henitai/compare/v0.5.0...v0.5.1
521
595
  [0.5.0]: https://github.com/martinotten/henitai/compare/v0.4.0...v0.5.0
522
596
  [0.4.0]: https://github.com/martinotten/henitai/compare/v0.3.1...v0.4.0
data/README.md CHANGED
@@ -77,7 +77,7 @@ excludes:
77
77
  - lib/henitai/eager_load.rb # standalone entry points with no in-process coverage
78
78
 
79
79
  test_excludes:
80
- - spec/end_to_end/*_spec.rb # never select these files for individual mutants
80
+ - spec/end_to_end/*_spec.rb # never run these files, mutants or coverage baseline
81
81
 
82
82
  mutation:
83
83
  operators: light # light | full
@@ -109,8 +109,14 @@ when a value is invalid.
109
109
 
110
110
  CLI flags override the corresponding values from `.henitai.yml`.
111
111
 
112
- `test_excludes` removes matching test files from per-mutant selection. A mutant
113
- with no tests left is reported as `NoCoverage` and is not executed.
112
+ `test_excludes` removes matching test files from per-mutant selection *and* from
113
+ the coverage-bootstrap suite. A mutant with no tests left is reported as
114
+ `NoCoverage` and is not executed. Excluding slow end-to-end or integration
115
+ suites therefore also keeps them out of the baseline run — which matters,
116
+ because a test that never runs against a mutant can never kill one, and the
117
+ baseline suite has a fixed timeout. Lines covered only by excluded tests count
118
+ as uncovered and are not mutated. Excluding every test file aborts with
119
+ `Henitai::ConfigurationError`.
114
120
  `mutation.max_log_bytes` is a positive per-stream byte cap for captured child
115
121
  output. `mutation.max_timeout` caps auto-calibrated timeouts; an explicit
116
122
  `mutation.timeout` still takes precedence. When JSON or HTML reporting is
@@ -32,11 +32,13 @@ module Henitai
32
32
  # @param config [Configuration]
33
33
  # @param integration [Integration::Base]
34
34
  # @param test_files [Array<String>, nil] test files to run; defaults to
35
- # all files reported by the integration when nil
35
+ # all files reported by the integration when nil.
36
+ # Either way, `config.test_excludes` matches are dropped.
37
+ # @raise [ConfigurationError] when the excludes leave no test file to run
36
38
  def ensure!(source_files:, config:, integration:, test_files: nil)
37
39
  return if source_files.empty?
38
40
 
39
- resolved_test_files = resolve_test_files(integration, test_files)
41
+ resolved_test_files = resolve_test_files(integration, test_files, config)
40
42
 
41
43
  # Skip the bootstrap only when the coverage artifacts are both newer than
42
44
  # all watched files and actually cover the configured sources. A fresh
@@ -225,10 +227,24 @@ module Henitai
225
227
  Array(source_files) + Array(test_files) + VerdictFingerprint.dependency_files
226
228
  end
227
229
 
228
- def resolve_test_files(integration, test_files)
229
- return test_files unless test_files.nil?
230
+ # Excluded tests are dropped here rather than in `bootstrap_coverage`, so
231
+ # the same filtered list feeds both the suite run and the freshness watch
232
+ # list: a test that no longer contributes to the baseline must not
233
+ # invalidate it either.
234
+ def resolve_test_files(integration, test_files, config)
235
+ candidates = test_files.nil? ? integration.test_files : test_files
236
+ patterns = configured_test_excludes(config)
237
+ retained = ExcludedTestFilter.new(patterns:).reject(candidates)
238
+ return retained unless retained.empty? && candidates.any?
239
+
240
+ raise ConfigurationError,
241
+ "test_excludes #{Array(patterns).inspect} excludes every test file; " \
242
+ "coverage cannot be generated"
243
+ end
230
244
 
231
- integration.test_files
245
+ # Specs pass bare config doubles exposing only what the example needs.
246
+ def configured_test_excludes(config)
247
+ config.respond_to?(:test_excludes) ? config.test_excludes : nil
232
248
  end
233
249
 
234
250
  def reports_dir(config)
@@ -71,6 +71,11 @@ module Henitai
71
71
  env
72
72
  end
73
73
 
74
+ # `pgroup: true` makes the suite child a group leader, so the whole tree
75
+ # it spawns -- a Rails test server, browser drivers -- is signalable as
76
+ # one group. Without it the parent's `kill(:SIGTERM, -pid)` addresses a
77
+ # group that does not exist, raises ESRCH, and leaves a timed-out suite
78
+ # running to completion while the parent has already given up on it.
74
79
  def spawn_suite_process(test_files, log_paths)
75
80
  FileUtils.mkdir_p(File.dirname(log_paths[:stdout_path]))
76
81
  File.open(log_paths[:stdout_path], "w") do |stdout_file|
@@ -79,16 +84,19 @@ module Henitai
79
84
  subprocess_env,
80
85
  *suite_command(test_files),
81
86
  out: stdout_file,
82
- err: stderr_file
87
+ err: stderr_file,
88
+ pgroup: true
83
89
  )
84
90
  end
85
91
  end
86
92
  end
87
93
 
94
+ # A timeout has already torn the group down and reaped the child in
95
+ # `handle_timeout`; signalling again would only race a recycled pid.
88
96
  def cleanup_suite_process(pid, wait_result)
89
97
  return unless pid
90
98
 
91
- cleanup_child_process(pid)
99
+ cleanup_process_group(pid) unless wait_result == :timeout
92
100
  reap_child(pid) if wait_result.nil?
93
101
  end
94
102
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Henitai
4
- VERSION = "0.5.1"
4
+ VERSION = "0.5.3"
5
5
  end
data/sig/henitai.rbs CHANGED
@@ -1211,6 +1211,8 @@ module Henitai
1211
1211
  def coverage_fresh?: (Array[String], Configuration, Integration::Base, Array[String]?) -> bool
1212
1212
  def coverage_report_path: (Configuration) -> String
1213
1213
  def bootstrap_coverage: (Integration::Base, Configuration, ?Array[String]?) -> void
1214
+ def resolve_test_files: (Integration::Base, Array[String]?, Configuration) -> Array[String]
1215
+ def configured_test_excludes: (Configuration) -> Array[String]?
1214
1216
  def source_file_paths: (Array[String]) -> Array[String]
1215
1217
  end
1216
1218
 
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.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Otten
@@ -252,7 +252,7 @@ metadata:
252
252
  changelog_uri: https://github.com/martinotten/henitai/blob/main/CHANGELOG.md
253
253
  documentation_uri: https://github.com/martinotten/henitai/blob/main/README.md
254
254
  homepage_uri: https://github.com/martinotten/henitai
255
- source_code_uri: https://github.com/martinotten/henitai/tree/v0.5.1
255
+ source_code_uri: https://github.com/martinotten/henitai/tree/v0.5.3
256
256
  rubygems_mfa_required: 'true'
257
257
  rdoc_options: []
258
258
  require_paths: