A differential run can carry a killed outcome onto a surviving mutant and exit 0
Maintainers usually reply within 3 days
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
Research direction
Read functions.mutation_id, _select, and _carry_forward, then trace how mutation IDs and form digests are built and reused. Reproduce the reported Go case with the 7-byte comment and compare it with --mutate-all and the 8-byte comment case. Done means the differential run preserves the surviving mutant’s outcome correctly and avoids rerunning unaffected functions; pull request #23 is already open.
Written by the indexing model from the issue text.
Description
A mutation id holds the site's byte offsets in the file. When text above an unchanged function moves it by exactly the distance between two identical sites in it, a site takes the id its neighbour had in the last run. It inherits that outcome without being run.
Repro (mutator a5a0cb8, crapper 6fd92c4, go 1.27.3, macOS 27)
go.mod: module example.com/shop
calc/calc.go:
package calc
func F(a, b, c, d int) (bool, bool) {
return a > b, c > d
}
calc/calc_test.go (it checks only the second result, so a > b → a >= b survives):
package calc
import "testing"
func TestF(t *testing.T) {
if _, y := F(0, 0, 1, 1); y {
t.Fatal("c > d")
}
}
$ ../mutator/mutator --no-coverage calc/calc.go
SURVIVED calc/calc.go:4 > -> >=
KILLED calc/calc.go:4 > -> >=
$ echo $?
3
The snapshot has […,"defn/F",62,63,">",">="] :survived and […,"defn/F",69,70,">",">="] :killed. Now add a 7-byte comment above F, the distance between the two >:
package calc
// ab
func F(a, b, c, d int) (bool, bool) {
return a > b, c > d
}
$ ../mutator/mutator --no-coverage calc/calc.go
KILLED calc/calc.go:6 > -> >=
KILLED calc/calc.go:6 > -> >=
$ echo $?
0
With --verbose, only one mutant runs. The first > is now at 69..70: it takes the killed id and is carried forward. --mutate-all on the same file reports the survivor again and exits 3, as does an 8-byte comment instead of 7.
Cause, as far as I can tell
functions.mutation_id uses site.start and site.end, which are offsets in the file. _select and _carry_forward reuse an outcome when the form's digest is unchanged and the id matches. The digest covers only the function's text, so it does not change when the function moves, while the offsets do.
Why it matters
Used as a gate, the run reports 100% and exits 0 with a surviving mutant. In Go a function often repeats a site, such as err != nil, so an edit above it can line up with a gap between two of them. The same offsets also make every function below an edit rerun all its mutants, which loses most of the gain of a differential run.
Possible fix
Key the id by offsets relative to the start of its form, or by the site's ordinal among identical sites in the form, rather than by offsets in the file.
- Dominant language
- Python
- Stars
- 55
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from unclebob/mutator
-
Difficulty 1/5 Under an hour Newbie friendliness 83/100
Maintainers usually reply within 3 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 3 days
-
Workers leave out source in a directory named build, out, dist, target or coverage, so every mutant is reported KILLEDPossibly taken @lohmag claimed this 2 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 3 days
-
Edits made to the file under test during a run are reverted when the run endsPossibly taken @andrewmolyuk claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 35/100
Maintainers usually reply within 3 days
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
Maintainers usually reply within 3 days
All issues in unclebob/mutator
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
pytest-dev/pytest-html#1085 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
pollen-robotics/reachy_mini#1527 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 66/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
A seed and its negative give the same run, so realizations of a negative seed repeat each otherOpen
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day