henitai 0.5.0 → 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: 34e96b6bb2bd74ac49e52a2acbe459ccb551498914d8b442f56c427c8b8ef45c
4
- data.tar.gz: 2792932da92f73f6e4cfcc0a67aa28dca405fd4cee6d02437e13f76fbd9ee08d
3
+ metadata.gz: 0530ecd98105a394b4466cbe23704f848565c55e318c35a31e096eedc2cba299
4
+ data.tar.gz: 56c64c5b679b0bc7c66691a4a7b20c274ac90d1d812d7abb65e778190cd6bb39
5
5
  SHA512:
6
- metadata.gz: 5fe64e42c96aaa19d26bdf1a45631146bea7cb6d8caca5891d75ac6da0bdb3c30843ea8a83e34a67d0f90559fe41229d46a7000478cdd3708fb7929efea9f209
7
- data.tar.gz: 39432815b88d834f6eb30be76255240b8124339505a86d8f644f2cd48ad787d85c6eb9ea9e7a85191262540e06ac467a9c32ad326a306e10953d9e758355d7bf
6
+ metadata.gz: e8725233ea8c806ca65fc488560daafbfc2300f723a054039b29dd45625a935217ebdbe609fde3e2df693299ee5ca069d35ce00b63a4e9a4d90dcfa9e77e2920
7
+ data.tar.gz: d8052c091f3967d1cc873cb34b02f04ad339714f08950841f65e7ef1ff064573bb0f54226eb3725d29dd3e7a42727d535b469396c76aa0a0ae2ffb5ae1075336
data/CHANGELOG.md CHANGED
@@ -7,9 +7,53 @@ 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
+
42
+ ## [0.5.1] - 2026-08-25
43
+
44
+ ### Changed
45
+
46
+ - `minitest` runtime dependency widened from `~> 5.25` to `>= 5.25, < 7`.
47
+ The floor is unchanged; only the ceiling moved. `~> 5.25` capped the
48
+ range below 6.0, so bundles resolving minitest 6.x could not install
49
+ henitai even though the integration makes no 5.x-only assumptions.
50
+ `spec/infra/gemspec_dependencies_spec.rb` now asserts both ends of the
51
+ range so a regression fails the suite rather than shipping quietly
52
+
10
53
  ## [0.5.0] - 2026-08-21
11
54
 
12
55
  ### Security
56
+
13
57
  - `sqlite3` requirement raised from `~> 1.7` to `>= 2.9.5, < 3`, excluding the
14
58
  vulnerable releases up to and including 2.9.4. This is a runtime major bump:
15
59
  applications pinned to `sqlite3` 1.x will need to upgrade before installing
@@ -18,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
62
  floors so a regression fails the suite rather than shipping quietly
19
63
 
20
64
  ### Changed
65
+
21
66
  - `coverage_criteria` now actually affects scoring. The block was documented,
22
67
  defaulted and validated since it was introduced, but nothing ever read it —
23
68
  `Result` hardcoded the `MS` numerator as `killed + timeout + runtime_error`,
@@ -40,6 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
40
85
  4.0-only form), and CI runs the floor alongside 4.0.2
41
86
 
42
87
  ### Fixed
88
+
43
89
  - Forked mutant children no longer outlive a parent that dies without warning.
44
90
  Children run in their own process group, and all parent-side cleanup —
45
91
  timeout kills, graceful drain, signal traps — only runs while the parent's
@@ -74,6 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
74
120
  0.3.1; this restores it
75
121
 
76
122
  ### Internal
123
+
77
124
  - Test-suite coupling to private implementation details is now guarded by
78
125
  `spec/infra/private_method_reach_spec.rb`, a ratchet: every spec that reaches
79
126
  a private method through `send` or an instance-variable poke carries a
@@ -94,6 +141,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
94
141
  ## [0.4.0] - 2026-07-14
95
142
 
96
143
  ### Added
144
+
97
145
  - `hard` operator set (`mutation.operators: hard` / `--operators hard`), a
98
146
  strict superset of `full` for usually-unkillable mutations (ADR-12):
99
147
  currently `EqualityIdentityOperator` and the new `HashKeyType`
@@ -103,6 +151,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
103
151
  single-pair hashes and double-splat entries are skipped
104
152
 
105
153
  ### Changed
154
+
106
155
  - `full` now means "usually killable": the symbol-key -> string-key mutation
107
156
  moved from `HashLiteral` into the new hard-set `HashKeyType`, and
108
157
  `EqualityIdentityOperator` moved from `full` to `hard` — `full` runs emit
@@ -111,6 +160,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
111
160
  ## [0.3.1] - 2026-07-13
112
161
 
113
162
  ### Fixed
163
+
114
164
  - Per-mutant log capture no longer crashes with
115
165
  `Encoding::UndefinedConversionError` when the host app sets
116
166
  `Encoding.default_internal` (as Rails test environments do) and a child
@@ -120,6 +170,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
120
170
  from corrupting the file.
121
171
 
122
172
  ### Changed
173
+
123
174
  - `--since REF` now includes the working tree: tracked files with
124
175
  uncommitted changes and untracked files count as changed, matching what
125
176
  actually gets tested. Previously only committed changes in `REF..HEAD`
@@ -136,6 +187,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
136
187
  ## [0.3.0] - 2026-07-13
137
188
 
138
189
  ### Added
190
+
139
191
  - `henitai run --incremental`: verdict reuse from the history store. Killed
140
192
  verdicts are reused when the subject's source and every covering test file
141
193
  are byte-identical to what was recorded; Survived verdicts are additionally
@@ -171,6 +223,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
171
223
  moved out of the default light set into its own operator (ADR-10)
172
224
 
173
225
  ### Fixed
226
+
174
227
  - Mutants in `module_function` modules were unkillable: `module_function`
175
228
  copies the method onto the module's singleton and activation only replaced
176
229
  the instance side, so callers kept running original code and every such
@@ -190,6 +243,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
190
243
  - Flaky-retry counter no longer inflates when a respawned child fails again
191
244
 
192
245
  ### Changed
246
+
193
247
  - Terminal reporter renders original nodes from their source slice and reuses
194
248
  a memoized unparse of the mutated node shared with the JSON reporter
195
249
  (a dogfood profile spent 27% of wall time re-unparsing survivors)
@@ -203,6 +257,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
203
257
  redefinition warnings into their logs
204
258
 
205
259
  ### Internal
260
+
206
261
  - Architecture documentation gained §8.11 (incremental verdict reuse) and
207
262
  ADR-11 (content-fingerprint verdict reuse, not git scoping); new backlog
208
263
  tickets for CI warm verdict cache, child null formatter, and auto-`--since`
@@ -214,12 +269,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
214
269
  ## [0.2.1] - 2026-06-25
215
270
 
216
271
  ### Fixed
272
+
217
273
  - `henitai run` now exits 0 when a run evaluates no valid mutants, instead of
218
274
  failing a CI gate on a vacuous result
219
275
  - Flaky-retry counts are now recorded on the parallel execution path (they were
220
276
  always reported as 0 regardless of actual retries)
221
277
 
222
278
  ### Changed
279
+
223
280
  - `Result` and `Reporter::Json` take their IO as an injected dependency,
224
281
  restoring the domain/infrastructure boundary (no public API change)
225
282
  - Decomposed the monolithic `integration.rb` into single-responsibility files
@@ -228,6 +285,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
228
285
  - Narrowed broad rescues in `safe_unparse` and per-test coverage snapshotting
229
286
 
230
287
  ### Internal
288
+
231
289
  - De-mocked runner specs, added `process_wakeup` and helper coverage, and
232
290
  removed sleep- and chdir-based flakiness from the suite
233
291
  - Enabled full-operator dogfooding configuration and isolated the
@@ -237,6 +295,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
237
295
  ## [0.2.0] - 2026-04-30
238
296
 
239
297
  ### Added
298
+
240
299
  - `--survivors-from <path>` flag on `henitai run` for survivor-only reruns:
241
300
  re-execute only the mutants that survived a prior full run without re-running
242
301
  the whole suite
@@ -271,6 +330,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
271
330
  - x86_64 platform added to gem platform list
272
331
 
273
332
  ### Changed
333
+
274
334
  - JSON mutation report vendor extension now always includes `sessionId`
275
335
  (and `gitSha` when available) to support survivor-only reruns
276
336
  - Terminal reporter labels partial reruns as "Partial survivor rerun" and
@@ -285,6 +345,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
285
345
  `max_flaky_retries: 3` added
286
346
 
287
347
  ### Fixed
348
+
288
349
  - Minitest autorun hook suppressed in mutation child processes to prevent
289
350
  spurious re-runs of the full suite inside each fork
290
351
  - Coverage bootstrap RSpec subprocess ARGV leakage resolved — child processes
@@ -297,6 +358,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
297
358
  preventing stale cache hits after edits
298
359
 
299
360
  ### Performance
361
+
300
362
  - File discovery cached in the integration layer; repeated calls within one
301
363
  run no longer re-scan the filesystem
302
364
  - Polling sleep removed from the scheduler hot loop; slots are refilled
@@ -305,6 +367,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
305
367
  ## [0.1.10] - 2026-04-16
306
368
 
307
369
  ### Fixed
370
+
308
371
  - RuboCop `RSpec/MultipleExpectations` offenses in `coverage_formatter_spec` and
309
372
  `per_test_coverage_collector_spec` resolved by splitting each two-assertion
310
373
  example into focused single-expectation examples
@@ -312,6 +375,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
312
375
  ## [0.1.9] - 2026-04-16
313
376
 
314
377
  ### Fixed
378
+
315
379
  - SimpleCov is now suppressed during Minitest mutant child runs: `SimpleCov.start`
316
380
  is turned into a no-op before test files are required, eliminating the
317
381
  "Stopped processing SimpleCov as a previous error has been detected" warning
@@ -320,12 +384,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
320
384
  ## [0.1.8] - 2026-04-16
321
385
 
322
386
  ### Added
387
+
323
388
  - Minitest integration now supports per-test coverage: a `MinitestCoverageReporter`
324
389
  snapshots `Coverage.peek_result` after each test and writes `henitai_per_test.json`,
325
390
  enabling targeted mutation runs that only execute the tests covering the mutated lines —
326
391
  the same efficiency that was previously available only to RSpec projects
327
392
 
328
393
  ### Fixed
394
+
329
395
  - RSpec integration now respects `--exclude-pattern` entries in `.rspec` so excluded
330
396
  spec files are not passed to the runner during mutation runs
331
397
  - Per-test source file filter corrected to check only the project-relative path prefix
@@ -335,11 +401,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
335
401
  ## [0.1.7] - 2026-04-14
336
402
 
337
403
  ### Added
404
+
338
405
  - Committed integration smoke projects for RSpec and Minitest, runnable via
339
406
  `bundle exec rake smoke:integration:all`, to exercise `henitai` against
340
407
  small real projects that depend on the local source checkout
341
408
 
342
409
  ### Fixed
410
+
343
411
  - Child-process stdout/stderr restoration after captured test runs now keeps
344
412
  the standard streams usable, preventing passing mutant executions from being
345
413
  misclassified because the child exited with a closed stdio handle
@@ -350,6 +418,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
350
418
  ## [0.1.6] - 2026-04-14
351
419
 
352
420
  ### Fixed
421
+
353
422
  - Minitest is no longer required eagerly when `henitai/integration` loads, so
354
423
  projects that do not include Minitest in their bundle can still boot and use
355
424
  non-Minitest integrations without a parent-process `LoadError`
@@ -357,6 +426,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
357
426
  ## [0.1.5] - 2026-04-14
358
427
 
359
428
  ### Fixed
429
+
360
430
  - Steep type errors in `Runner` after removing targeted-run bootstrap scoping:
361
431
  updated `bootstrap_mutants` RBS signature to match the new single-argument
362
432
  form and removed stale signatures for `refresh_coverage_for_targeted_run`,
@@ -365,6 +435,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
365
435
  workspace setup and report writing into helper methods
366
436
 
367
437
  ### Changed
438
+
368
439
  - Targeted-run coverage bootstrap no longer scopes the initial run to the
369
440
  subject's test files; the full suite is always used for the baseline,
370
441
  trading a minor performance optimisation for reliability
@@ -372,6 +443,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
372
443
  ## [0.1.4] - 2026-04-14
373
444
 
374
445
  ### Fixed
446
+
375
447
  - `StringLiteral` operator no longer generates no-op mutations where the
376
448
  replacement equals the original value (e.g. the spurious `"" → ""` mutant
377
449
  that was emitted for methods already returning an empty string literal)
@@ -387,6 +459,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
387
459
  runner's configured reports directory
388
460
 
389
461
  ### Changed
462
+
390
463
  - `ScenarioExecutionResult.build` factory method consolidates status and
391
464
  exit-status derivation that was previously spread across `Integration`,
392
465
  reducing the mutation surface of the value object
@@ -394,6 +467,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
394
467
  ## [0.1.3] - 2026-04-13
395
468
 
396
469
  ### Added
470
+
397
471
  - Four new mutation operators: `UnaryOperator` (negates boolean and numeric
398
472
  unary expressions), `UpdateOperator` (swaps `+=`/`-=`/`*=` and targets
399
473
  compound-assignment nodes), `RegexMutator` (replaces regex literals with
@@ -412,6 +486,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
412
486
  `x || false`, `true && x`, `x && true` are suppressed as equivalent mutants
413
487
 
414
488
  ### Changed
489
+
415
490
  - Per-line mutation cap (`max_mutants_per_line`) removed from the generator,
416
491
  configuration schema, and validator — see ADR-08. All syntactically valid
417
492
  mutations on a line are now generated unconditionally
@@ -426,6 +501,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
426
501
  without reflection
427
502
 
428
503
  ### Performance
504
+
429
505
  - Coverage bootstrap freshness check: the baseline RSpec run is skipped when
430
506
  `.resultset.json` is newer than every watched source and test file,
431
507
  eliminating ~83 % of bootstrap wall time on repeated runs within a session
@@ -447,6 +523,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
447
523
  AST node
448
524
 
449
525
  ### Fixed
526
+
450
527
  - Mutant child processes now run in isolated process groups (`setpgid`);
451
528
  `cleanup_process_group` sends `SIGTERM` to the entire group on timeout or
452
529
  error, preventing orphaned subprocesses
@@ -462,10 +539,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
462
539
  ## [0.1.2] - 2026-04-07
463
540
 
464
541
  ### Added
542
+
465
543
  - Method coverage is now enabled in both RSpec and Minitest bootstraps, and
466
544
  the static filter merges method-level coverage into the line map
467
545
 
468
546
  ### Fixed
547
+
469
548
  - Coverage baseline regeneration now happens on every `henitai run`, so stale
470
549
  coverage state does not leak between runs
471
550
  - Coverage handling now accepts symbol-keyed `Coverage.peek_result` output and
@@ -482,6 +561,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
482
561
  ## [0.1.1] - 2026-04-03
483
562
 
484
563
  ### Added
564
+
485
565
  - Minitest integration for Rails projects: injects SimpleCov for coverage
486
566
  collection, sets `RAILS_ENV=test` and `PARALLEL_WORKERS=1` in the baseline
487
567
  subprocess, preloads `config/environment.rb` before mutant activation, adds
@@ -489,6 +569,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
489
569
  - `simplecov` runtime dependency (required by the Minitest integration)
490
570
 
491
571
  ### Fixed
572
+
492
573
  - `rspec/core` was unconditionally required at load time, causing a `LoadError`
493
574
  in projects that do not have RSpec installed — now loaded lazily only when the
494
575
  RSpec integration is used
@@ -498,6 +579,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
498
579
  ## [0.1.0] - 2026-03-01
499
580
 
500
581
  ### Added
582
+
501
583
  - Initial gem scaffold with Ruby 4.0.2 support
502
584
  - Dev Container configuration (official `ruby:4.0.2-alpine` base image, Codex CLI preinstalled)
503
585
  - CI pipeline (RuboCop + RSpec + incremental mutation testing on PRs)
@@ -506,7 +588,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
506
588
  - CLI critical path: `henitai run` now executes the full pipeline, supports `--since`, returns CI-friendly exit codes, and `henitai version` prints `Henitai::VERSION`
507
589
  - RSpec per-test coverage output: `henitai/coverage_formatter` now writes `coverage/henitai_per_test.json`
508
590
 
509
- [Unreleased]: https://github.com/martinotten/henitai/compare/v0.5.0...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
594
+ [0.5.1]: https://github.com/martinotten/henitai/compare/v0.5.0...v0.5.1
510
595
  [0.5.0]: https://github.com/martinotten/henitai/compare/v0.4.0...v0.5.0
511
596
  [0.4.0]: https://github.com/martinotten/henitai/compare/v0.3.1...v0.4.0
512
597
  [0.3.1]: https://github.com/martinotten/henitai/compare/v0.3.0...v0.3.1
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.0"
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.0
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Otten
@@ -13,16 +13,22 @@ dependencies:
13
13
  name: minitest
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '5.25'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '7'
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
22
25
  requirements:
23
- - - "~>"
26
+ - - ">="
24
27
  - !ruby/object:Gem::Version
25
28
  version: '5.25'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '7'
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: prism
28
34
  requirement: !ruby/object:Gem::Requirement
@@ -246,7 +252,7 @@ metadata:
246
252
  changelog_uri: https://github.com/martinotten/henitai/blob/main/CHANGELOG.md
247
253
  documentation_uri: https://github.com/martinotten/henitai/blob/main/README.md
248
254
  homepage_uri: https://github.com/martinotten/henitai
249
- source_code_uri: https://github.com/martinotten/henitai/tree/v0.5.0
255
+ source_code_uri: https://github.com/martinotten/henitai/tree/v0.5.3
250
256
  rubygems_mfa_required: 'true'
251
257
  rdoc_options: []
252
258
  require_paths: