Repository navigation
Description
What happens
In a Go module that is part of a go.work workspace, or that points at a sibling module with a relative replace directory, no mutant can build inside its worker. Every mutant fails with [setup failed] and counts as KILLED, so the package scores 100% even though no test ran (the symptom in #18, with a different cause, like #15).
Repro
mutator a5a0cb8, crapper 6fd92c4, Go 1.27.3, Linux.
repo/
go.work use ./lib ./svc
lib/go.mod module example.com/acme/lib
lib/lib.go package lib; func Zero() int { return 0 }
svc/go.mod module example.com/acme/svc
require example.com/acme/lib v0.0.0
replace example.com/acme/lib => ../lib
svc/calc/calc.go
svc/calc/calc_test.go
svc/calc/calc.go:
package calc
import "example.com/acme/lib"
func Max(a, b int) int {
if a > b {
return a
}
return b + lib.Zero()
}svc/calc/calc_test.go:
package calc
import "testing"
func TestMax(t *testing.T) {
if Max(2, 1) != 2 || Max(1, 2) != 2 {
t.Fatal("wrong max")
}
}Both mutation sites are equivalent mutants (>= gives the same Max, and b - 0 == b + 0), so both should survive. go test ./calc passes in svc/.
$ cd svc && mutator --no-coverage --mutate-all calc/calc.go
KILLED calc/calc.go:6 > -> >=
KILLED calc/calc.go:9 + -> -
Total 2 0 0 2 100.0%
$ GOWORK=off mutator --no-coverage --mutate-all calc/calc.go
Total 2 0 0 2 100.0%
Control: the same function in a single module with no workspace and no replace reports both sites SURVIVED.
Cause, as far as I can tell
The worker is a directory under target/mutation-workers/ with the module's files linked into it. Running go test there, by hand in an equivalent directory:
-
With the workspace active, Go resolves the worker to the original module, whose directory does not contain the worker path:
main module (example.com/acme/svc) does not contain package example.com/acme/svc/target/probe/calc FAIL ./calc [setup failed] -
With
GOWORK=off, the relativereplaceis resolved from the worker directory, where../libdoes not exist:calc/calc.go:3:8: example.com/acme/lib@v0.0.0: replacement directory ../lib does not exist FAIL example.com/acme/svc/calc [setup failed]
Why it matters
Monorepos commonly use go.work, and modules often keep relative replace lines so they also build without the workspace. On those layouts, mutator reports 100% for every package and exits 0.
Possible fix
- Give the worker its own workspace: write a
go.workinto the worker thatuses the worker directory plus the other workspace modules by absolute path, and setGOWORKto it. - For relative
replacedirectories, run with-modfilepointing at a copy ofgo.modwhose replace paths are made absolute. - Independently of this, Mutants that do not compile count as KILLED, which inflates the score (Go: + on strings) #18's suggestion (report
[setup failed]and[build failed]as their own outcome, left out of the score) would turn this from a silent 100% into a visible error.
Activity