Read Go coverage by module import path, the name go test gives a file.

Why: every Go run printed "No coverage data for <file>; its sites are
uncovered". `go test -coverprofile` keys files by import path
(`github.com/acme/tool/cmd/tool/main.go`); mutator looked them up by
absolute disk path (`/.../tool/cmd/tool/main.go`). Neither path ends with
the other, so no Go file ever matched and only --no-coverage ran sites.

What: `_go_lines` asks crapper's `go_import_name` for the profile's name
of the file (nearest go.mod module line plus path inside the module) and
`_lookup` tries that name as an exact key, then the existing suffix
match. Other languages pass no name and are unchanged. Needs crapper
c757ec7 or later.

Proof: two new specs (module at the root, module in a subdirectory with
a different name) failed with None first, now pass; 78 pass. crapper:
_go_lines 6.0, covered_lines 5.0, _lookup 4.0. dryer: no candidates.
mutator on the changed lines: one equivalent survivor (insert(0) vs
insert(1)) removed by appending the name; no survivors left.
This commit is contained in:
Robert C. Martin
2026-11-10 12:38:11 -06:00
parent a5a0cb803b
commit 830ec7e9c3
2 changed files with 52 additions and 5 deletions
+40
View File
@@ -96,3 +96,43 @@ def test_go_profile_does_not_cross_files(tmp_path):
profile.write_text("mode: set\na/b/widget.go:2.1,2.2 1 1\n", encoding="utf-8")
assert covered_lines(tmp_path, short, "go") is None
assert covered_lines(tmp_path, long, "go") == {2}
def _go_module(module_dir, module, profile_text):
module_dir.mkdir(parents=True, exist_ok=True)
(module_dir / "go.mod").write_text(f"module {module}\n\ngo 1.21\n", encoding="utf-8")
profile = module_dir / "target" / "coverage" / "go" / "coverage.out"
profile.parent.mkdir(parents=True)
profile.write_text(profile_text, encoding="utf-8")
def _go_source(path):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("package main\nfunc main() {}\n", encoding="utf-8")
return path
def test_go_profile_keyed_by_import_path_marks_hit_lines(tmp_path):
# `go test -coverprofile` names files by module import path, not by disk path.
_go_module(
tmp_path,
"github.com/acme/tool",
"mode: set\n"
"github.com/acme/tool/cmd/tool/main.go:2.1,3.2 1 1\n"
"github.com/acme/tool/cmd/tool/main.go:4.1,4.2 1 0\n"
"github.com/acme/tool/main.go:7.1,7.2 1 1\n",
)
nested = _go_source(tmp_path / "cmd" / "tool" / "main.go")
top = _go_source(tmp_path / "main.go")
assert covered_lines(tmp_path, nested, "go") == {2, 3}
assert covered_lines(tmp_path, top, "go") == {7}
def test_go_profile_of_a_module_in_a_subdirectory(tmp_path):
_go_module(
tmp_path / "svc",
"example.com/api",
"mode: set\nexample.com/api/calc/calc.go:4.2,6.1 1 1\n",
)
source = _go_source(tmp_path / "svc" / "calc" / "calc.go")
assert covered_lines(tmp_path, source, "go") == {4, 5, 6}