mutineer 0.9.0 → 0.9.1
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 -0
- data/lib/mutineer/coverage_map.rb +21 -0
- data/lib/mutineer/runner.rb +10 -2
- data/lib/mutineer/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d61c927b33961995a0d38b691cdf0b34a6e9041213980c0cd7a3a01f457f875
|
|
4
|
+
data.tar.gz: 8e4f32e923344837ab664805a3099a9e67d405bbe7b63d83986d1af0b488ead5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ea750d511b52ef0f9d320c39ef0b4a3b51189116c09811b3bf4f2d5752b026d1ddc62411387a261f6f60ba73e23e2b76af1e553a745bd63f9a3d56a0cd068cb
|
|
7
|
+
data.tar.gz: 6dc10950ea34998e459e5007095bd43df87a633a4ca18e19afee1812a8fed154e086f3ae3a7b808bd84ba93336bb61208195d001efd846fea367502a23167ef2
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.9.1] - 2026-07-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Per-method uncapturable granularity** (#25) — the `:uncapturable` taint was
|
|
11
|
+
whole-file, so a method reachable only by a *failed* capture in an otherwise-
|
|
12
|
+
covered file was mislabeled `no_coverage`. It's now attributed per method (by
|
|
13
|
+
the method's body coverage), so only the affected method is tainted. Fully-
|
|
14
|
+
failed files are unchanged.
|
|
15
|
+
|
|
7
16
|
## [0.9.0] - 2026-06-30
|
|
8
17
|
|
|
9
18
|
### Added
|
|
@@ -175,6 +184,7 @@ Rails hardening + CI batch (issues #8–#13), all verified Rails-free.
|
|
|
175
184
|
- `.mutineer.yml` configuration (CLI > config > default precedence).
|
|
176
185
|
- Byte-correct source handling for multibyte (UTF-8) sources.
|
|
177
186
|
|
|
187
|
+
[0.9.1]: https://github.com/davidteren/mutineer/releases/tag/v0.9.1
|
|
178
188
|
[0.9.0]: https://github.com/davidteren/mutineer/releases/tag/v0.9.0
|
|
179
189
|
[0.8.0]: https://github.com/davidteren/mutineer/releases/tag/v0.8.0
|
|
180
190
|
[0.7.1]: https://github.com/davidteren/mutineer/releases/tag/v0.7.1
|
|
@@ -87,6 +87,27 @@ module Mutineer
|
|
|
87
87
|
failed_test_targets.include?(File.basename(rel, ".rb"))
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# #25: per-METHOD taint. A mutant on a line whose enclosing method got zero
|
|
91
|
+
# successful coverage, in a file a failed sibling test targets, is
|
|
92
|
+
# :uncapturable (the capture that would have covered it errored) — NOT a
|
|
93
|
+
# genuine gap. A method with any covered line means its uncovered lines are a
|
|
94
|
+
# real :no_coverage. A failed capture emits no coverage, so per-line intent is
|
|
95
|
+
# unknowable; method-range + successful coverage is the finest derivable signal.
|
|
96
|
+
# Fully-failed files behave exactly as uncapturable_source? did (every method
|
|
97
|
+
# range has zero coverage), so #8/#9/#19 behavior is unchanged.
|
|
98
|
+
#
|
|
99
|
+
# @param file [String] source file path.
|
|
100
|
+
# @param line_range [Range] 1-based enclosing-method line range.
|
|
101
|
+
# @return [Boolean]
|
|
102
|
+
def method_uncapturable?(file, line_range)
|
|
103
|
+
return false if @failed_test_files.empty?
|
|
104
|
+
|
|
105
|
+
rel = relativize(absolute(file))
|
|
106
|
+
return false unless failed_test_targets.include?(File.basename(rel, ".rb"))
|
|
107
|
+
|
|
108
|
+
line_range.none? { |ln| @map.key?("#{rel}:#{ln}") }
|
|
109
|
+
end
|
|
110
|
+
|
|
90
111
|
private
|
|
91
112
|
|
|
92
113
|
# Source rel-paths that received coverage from any successful capture.
|
data/lib/mutineer/runner.rb
CHANGED
|
@@ -259,10 +259,18 @@ module Mutineer
|
|
|
259
259
|
# covering test files run in the child.
|
|
260
260
|
line = source.byteslice(0, mutation.start_offset).count("\n") + 1
|
|
261
261
|
chosen = coverage_map.tests_for(source_file, line)
|
|
262
|
-
# #9: distinguish a genuine coverage gap from a line whose would-be test
|
|
262
|
+
# #9/#25: distinguish a genuine coverage gap from a line whose would-be test
|
|
263
263
|
# errored during capture (coverage lost) — the latter is :uncapturable.
|
|
264
|
+
# #25: taint per-METHOD (the mutant's enclosing def range), not whole-file,
|
|
265
|
+
# so a covered method's uncovered line stays :no_coverage while a method
|
|
266
|
+
# reachable only by a failed capture is :uncapturable.
|
|
264
267
|
if chosen.empty?
|
|
265
|
-
|
|
268
|
+
# Use the method BODY range, not the whole def: the `def`/`end` lines are
|
|
269
|
+
# "covered" at class-load even when the body never runs, which would mask
|
|
270
|
+
# an uncovered method. body_loc is the body statements' span.
|
|
271
|
+
loc = subject&.body_loc
|
|
272
|
+
range = loc ? (loc.start_line..loc.end_line) : (line..line)
|
|
273
|
+
return coverage_map.method_uncapturable?(source_file, range) ? Result.uncapturable : Result.no_coverage
|
|
266
274
|
end
|
|
267
275
|
|
|
268
276
|
abs_tests = chosen.map { |t| File.expand_path(t, coverage_map.project_root) }
|
data/lib/mutineer/version.rb
CHANGED