Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for extra env variables in go tests (Cherry-pick of #16013) #16067

Merged
merged 1 commit into from Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -35,7 +35,7 @@
third_party_pkg,
)
from pants.build_graph.address import Address
from pants.core.goals.test import TestResult
from pants.core.goals.test import TestResult, get_filtered_environment
from pants.core.util_rules import config_files, source_files, stripped_source_files
from pants.core.util_rules.external_tool import rules as external_tool_rules
from pants.engine.fs import Digest, DigestContents
Expand Down Expand Up @@ -70,6 +70,7 @@ def rule_runner() -> RuleRunner:
*target_type_rules.rules(),
*tests_analysis.rules(),
*third_party_pkg.rules(),
get_filtered_environment,
QueryRule(HydratedSources, [HydrateSourcesRequest]),
QueryRule(GeneratedSources, [GenerateGoFromProtobufRequest]),
QueryRule(DigestContents, (Digest,)),
Expand Down
23 changes: 21 additions & 2 deletions src/python/pants/backend/go/goals/test.py
Expand Up @@ -10,6 +10,7 @@
from pants.backend.go.subsystems.gotest import GoTestSubsystem
from pants.backend.go.target_types import (
GoPackageSourcesField,
GoTestExtraEnvVarsField,
GoTestTimeoutField,
SkipGoTestsField,
)
Expand All @@ -31,12 +32,14 @@
from pants.core.goals.test import (
TestDebugAdapterRequest,
TestDebugRequest,
TestExtraEnv,
TestFieldSet,
TestResult,
TestSubsystem,
)
from pants.core.target_types import FileSourceField
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
from pants.engine.environment import Environment, EnvironmentRequest
from pants.engine.fs import EMPTY_FILE_DIGEST, AddPrefix, Digest, MergeDigests
from pants.engine.process import FallibleProcessResult, Process, ProcessCacheScope
from pants.engine.rules import Get, MultiGet, collect_rules, rule
Expand Down Expand Up @@ -85,6 +88,7 @@ class GoTestFieldSet(TestFieldSet):
sources: GoPackageSourcesField
dependencies: Dependencies
timeout: GoTestTimeoutField
extra_env_vars: GoTestExtraEnvVarsField

@classmethod
def opt_out(cls, tgt: Target) -> bool:
Expand Down Expand Up @@ -148,7 +152,10 @@ def transform_test_args(args: Sequence[str], timeout_field_value: int | None) ->

@rule(desc="Test with Go", level=LogLevel.DEBUG)
async def run_go_tests(
field_set: GoTestFieldSet, test_subsystem: TestSubsystem, go_test_subsystem: GoTestSubsystem
field_set: GoTestFieldSet,
test_subsystem: TestSubsystem,
go_test_subsystem: GoTestSubsystem,
test_extra_env: TestExtraEnv,
) -> TestResult:
maybe_pkg_analysis, maybe_pkg_digest, dependencies = await MultiGet(
Get(FallibleFirstPartyPkgAnalysis, FirstPartyPkgAnalysisRequest(field_set.address)),
Expand Down Expand Up @@ -289,7 +296,10 @@ def compilation_failure(exit_code: int, stdout: str | None, stderr: str | None)
# This allows tests to open dependencies on `file` targets regardless of where they are
# located. See https://dave.cheney.net/2016/05/10/test-fixtures-in-go.
working_dir = field_set.address.spec_path
binary_with_prefix, files_sources = await MultiGet(
field_set_extra_env_get = Get(
Environment, EnvironmentRequest(field_set.extra_env_vars.value or ())
)
binary_with_prefix, files_sources, field_set_extra_env = await MultiGet(
Get(Digest, AddPrefix(binary.digest, working_dir)),
Get(
SourceFiles,
Expand All @@ -299,11 +309,19 @@ def compilation_failure(exit_code: int, stdout: str | None, stderr: str | None)
enable_codegen=True,
),
),
field_set_extra_env_get,
)
test_input_digest = await Get(
Digest, MergeDigests((binary_with_prefix, files_sources.snapshot.digest))
)

extra_env = {
**test_extra_env.env,
# NOTE: field_set_extra_env intentionally after `test_extra_env` to allow overriding within
# `go_package`.
**field_set_extra_env,
}

cache_scope = (
ProcessCacheScope.PER_SESSION if test_subsystem.force else ProcessCacheScope.SUCCESSFUL
)
Expand All @@ -315,6 +333,7 @@ def compilation_failure(exit_code: int, stdout: str | None, stderr: str | None)
"./test_runner",
*transform_test_args(go_test_subsystem.args, field_set.timeout.value),
],
env=extra_env,
input_digest=test_input_digest,
description=f"Run Go tests: {field_set.address}",
cache_scope=cache_scope,
Expand Down
71 changes: 70 additions & 1 deletion src/python/pants/backend/go/goals/test_test.py
Expand Up @@ -24,7 +24,7 @@
third_party_pkg,
)
from pants.backend.go.util_rules.sdk import GoSdkProcess
from pants.core.goals.test import TestResult
from pants.core.goals.test import TestResult, get_filtered_environment
from pants.core.target_types import FileTarget
from pants.core.util_rules import source_files
from pants.engine.addresses import Address
Expand All @@ -48,6 +48,7 @@ def rule_runner() -> RuleRunner:
*tests_analysis.rules(),
*third_party_pkg.rules(),
*source_files.rules(),
get_filtered_environment,
QueryRule(TestResult, [GoTestFieldSet]),
QueryRule(ProcessResult, [GoSdkProcess]),
],
Expand Down Expand Up @@ -543,6 +544,74 @@ def test_fuzz_target_supported(rule_runner: RuleRunner) -> None:
assert "PASS: FuzzFoo" in result.stdout


def test_extra_env_vars(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"foo/BUILD": textwrap.dedent(
"""
go_mod(name='mod')
go_package(
test_extra_env_vars=(
"GO_PACKAGE_VAR_WITHOUT_VALUE",
"GO_PACKAGE_VAR_WITH_VALUE=go_package_var_with_value",
"GO_PACKAGE_OVERRIDE_WITH_VALUE_VAR=go_package_override_with_value_var_override",
)
)
"""
),
"foo/go.mod": "module foo",
"foo/add.go": textwrap.dedent(
"""
package foo
import "os"
func envIs(e, v string) bool {
return (os.Getenv(e) == v)
}
"""
),
"foo/add_test.go": textwrap.dedent(
"""
package foo
import "testing"
func TestEnvs(t *testing.T) {
if !envIs("ARG_WITH_VALUE_VAR", "arg_with_value_var") {
t.Fail()
}
if !envIs("ARG_WITHOUT_VALUE_VAR", "arg_without_value_var") {
t.Fail()
}
if !envIs("GO_PACKAGE_VAR_WITH_VALUE", "go_package_var_with_value") {
t.Fail()
}
if !envIs("GO_PACKAGE_VAR_WITHOUT_VALUE", "go_package_var_without_value") {
t.Fail()
}
if !envIs("GO_PACKAGE_OVERRIDE_WITH_VALUE_VAR", "go_package_override_with_value_var_override") {
t.Fail()
}
}
"""
),
}
)
tgt = rule_runner.get_target(Address("foo"))
rule_runner.set_options(
args=[
"--go-test-args=-v -bench=.",
'--test-extra-env-vars=["ARG_WITH_VALUE_VAR=arg_with_value_var", "ARG_WITHOUT_VALUE_VAR", "GO_PACKAGE_OVERRIDE_ARG_WITH_VALUE_VAR"]',
],
env={
"ARG_WITHOUT_VALUE_VAR": "arg_without_value_var",
"GO_PACKAGE_VAR_WITHOUT_VALUE": "go_package_var_without_value",
"GO_PACKAGE_OVERRIDE_WITH_VALUE_VAR": "go_package_override_with_value_var",
},
env_inherit={"PATH"},
)
result = rule_runner.request(TestResult, [GoTestFieldSet.create(tgt)])
assert result.exit_code == 0
assert "PASS: TestEnvs" in result.stdout


def test_skip_tests(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
Expand Down
14 changes: 14 additions & 0 deletions src/python/pants/backend/go/target_types.py
Expand Up @@ -21,6 +21,7 @@
InvalidTargetException,
MultipleSourcesField,
StringField,
StringSequenceField,
Target,
TargetGenerator,
ValidNumbers,
Expand Down Expand Up @@ -179,6 +180,18 @@ class SkipGoTestsField(BoolField):
help = "If true, don't run this package's tests."


class GoTestExtraEnvVarsField(StringSequenceField):
alias = "test_extra_env_vars"
help = softwrap(
"""
Additional environment variables to include in test processes.
Entries are strings in the form `ENV_VAR=value` to use explicitly; or just
`ENV_VAR` to copy the value of a variable in Pants's own environment.
This will be merged with and override values from [test].extra_env_vars.
"""
)


class GoTestTimeoutField(IntField):
alias = "test_timeout"
help = softwrap(
Expand All @@ -197,6 +210,7 @@ class GoPackageTarget(Target):
*COMMON_TARGET_FIELDS,
GoPackageDependenciesField,
GoPackageSourcesField,
GoTestExtraEnvVarsField,
GoTestTimeoutField,
SkipGoTestsField,
)
Expand Down
Expand Up @@ -27,7 +27,7 @@
)
from pants.backend.go.util_rules.embedcfg import EmbedConfig
from pants.build_graph.address import Address
from pants.core.goals.test import TestResult
from pants.core.goals.test import TestResult, get_filtered_environment
from pants.core.target_types import ResourceTarget
from pants.core.util_rules import source_files
from pants.testutil.rule_runner import QueryRule, RuleRunner
Expand All @@ -49,6 +49,7 @@ def rule_runner() -> RuleRunner:
*tests_analysis.rules(),
*third_party_pkg.rules(),
*source_files.rules(),
get_filtered_environment,
QueryRule(TestResult, [GoTestFieldSet]),
],
target_types=[GoModTarget, GoPackageTarget, ResourceTarget],
Expand Down