From 8c420c90ad7ff4f189ff40e1f64c054b893694de Mon Sep 17 00:00:00 2001 From: Tom Dyas Date: Wed, 25 Sep 2024 16:43:03 -0400 Subject: [PATCH] golang: fix exception when coverage enabled and external tests import the base package (#21452) As reported in https://github.com/pantsbuild/pants/issues/21386, Pants raises an exception when coverage is enabled and a package has external tests (i.e., "xtests") which import the base package. The bug occurred because the code which injects the base package dependency did not properly pass through the coverage flag. Consequently, the compile graph ended up with two different variants of the base package: one with coverage codegen and the other without coverage codegen. This resulted in two different copies of the package archive which Pants cannot merge together into a single `Digest`. The solution is to properly pass through the coverage flag when injecting the base package dependency on the external test package to ensure there are not multiple variants of the base package in the compile graph. Fixes #21386. --- docs/notes/2.24.x.md | 4 ++ .../pants/backend/go/goals/test_test.py | 42 +++++++++++++++++++ .../backend/go/util_rules/build_pkg_target.py | 5 ++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/notes/2.24.x.md b/docs/notes/2.24.x.md index db947ae0cc0..6bbafdcf79f 100644 --- a/docs/notes/2.24.x.md +++ b/docs/notes/2.24.x.md @@ -79,6 +79,10 @@ Pants will now warn if any errors are encountered while fingerprinting candidate Added a new `cache_scope` field to `adhoc_tool` and `shell_command` targets to allow configuration of the "cache scope" of the invoked process. The cache scope determines how long Pants will cache the result of the invoked process absent any other invalidation of the result via source or dependency changes. +#### Go + +Fix a bug where Pants raised an internal exception which occurred when compiling a Go package with coverage support when the package also had an external test which imported the base package. + ### Plugin API changes The `path_metadata_request` intrinsic rule can now access metadata for paths in the local system outside of the build root. Use the new `namespace` field on `PathMetadataRequest` to request metdata on local system paths using namespace `PathNamespace.SYSTEM`. diff --git a/src/python/pants/backend/go/goals/test_test.py b/src/python/pants/backend/go/goals/test_test.py index 2b764a9cb55..26cfc9d6f97 100644 --- a/src/python/pants/backend/go/goals/test_test.py +++ b/src/python/pants/backend/go/goals/test_test.py @@ -714,3 +714,45 @@ def test_profile_options_write_results(rule_runner: RuleRunner) -> None: "test_runner", "trace.out", ] + + +def test_external_test_with_use_coverage(rule_runner: RuleRunner) -> None: + rule_runner.write_files( + { + "foo/BUILD": "go_mod(name='mod')\ngo_package()", + "foo/go.mod": "module foo", + "foo/add.go": textwrap.dedent( + """ + package foo + func Add(x, y int) int { + return x + y + } + """ + ), + "foo/add_test.go": textwrap.dedent( + """ + package foo_test + import ( + "foo" + "testing" + ) + func TestAdd(t *testing.T) { + if foo.Add(2, 3) != 5 { + t.Fail() + } + } + """ + ), + } + ) + tgt = rule_runner.get_target(Address("foo", generated_name="./")) + rule_runner.set_options( + [ + "--test-use-coverage", + ], + env_inherit={"PATH"}, + ) + result = rule_runner.request( + TestResult, [GoTestRequest.Batch("", (GoTestFieldSet.create(tgt),), None)] + ) + assert result.exit_code == 0 diff --git a/src/python/pants/backend/go/util_rules/build_pkg_target.py b/src/python/pants/backend/go/util_rules/build_pkg_target.py index fccc4290b7c..62964ac5d63 100644 --- a/src/python/pants/backend/go/util_rules/build_pkg_target.py +++ b/src/python/pants/backend/go/util_rules/build_pkg_target.py @@ -458,7 +458,10 @@ async def setup_build_go_package_target_request( maybe_base_pkg_dep = await Get( FallibleBuildGoPackageRequest, BuildGoPackageTargetRequest( - request.address, for_tests=True, build_opts=request.build_opts + request.address, + for_tests=True, + with_coverage=request.with_coverage, + build_opts=request.build_opts, ), ) if maybe_base_pkg_dep.request is None: