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

[internal] Test discovery of go binary #13038

Merged
merged 3 commits into from
Sep 29, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/python/pants/backend/go/subsystems/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
# Licensed under the Apache License, Version 2.0 (see LICENSE).

python_library()
python_tests(name="tests")
3 changes: 2 additions & 1 deletion src/python/pants/backend/go/subsystems/golang.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ def register_options(cls, register):
help=(
"The Go version you are using, such as `1.17`.\n\n"
"Pants will only use Go distributions from `--go-search-paths` that have the "
"expected version, and it will error if none are found. "
"expected version, and it will error if none are found.\n\n"
"Do not include the patch version."
),
)

Expand Down
116 changes: 116 additions & 0 deletions src/python/pants/backend/go/subsystems/golang_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import annotations

from pathlib import Path
from textwrap import dedent

import pytest

from pants.backend.go.subsystems.golang import GoRoot
from pants.backend.go.subsystems.golang import rules as golang_rules
from pants.engine.internals.scheduler import ExecutionError
from pants.engine.process import BinaryNotFoundError
from pants.engine.rules import QueryRule
from pants.testutil.rule_runner import RuleRunner
from pants.util.contextutil import temporary_dir

EXPECTED_VERSION = "1.17"


@pytest.fixture
def rule_runner() -> RuleRunner:
return RuleRunner(rules=[*golang_rules(), QueryRule(GoRoot, [])])


def get_goroot(rule_runner: RuleRunner, binary_names_to_scripts: list[tuple[str, str]]) -> GoRoot:
with temporary_dir() as tmpdir:
binary_dirs = []
for i, (name, script) in enumerate(binary_names_to_scripts):
binary_dir = Path(tmpdir, f"bin{i}")
binary_dir.mkdir()
binary_dirs.append(str(binary_dir))

binary_path = binary_dir / name
binary_path.write_text(script)
binary_path.chmod(0o777)

rule_runner.set_options(
[
f"--golang-go-search-paths={repr(binary_dirs)}",
f"--golang-expected-version={EXPECTED_VERSION}",
],
env_inherit={"PATH"},
)
return rule_runner.request(GoRoot, [])


def mock_go_binary(*, version_output: str, env_output: str) -> str:
"""Return a bash script that emulates `go version` and `go env`."""
return dedent(
f"""\
#!/bin/bash

if [[ "$1" == version ]]; then
echo '{version_output}'
else
echo '{env_output}'
fi
"""
)


def test_find_valid_binary(rule_runner: RuleRunner) -> None:
valid_without_patch = mock_go_binary(
version_output=f"go version go{EXPECTED_VERSION} darwin/arm64",
env_output="/valid/binary",
)
assert get_goroot(rule_runner, [("go", valid_without_patch)]).path == "/valid/binary"

valid_with_patch = mock_go_binary(
version_output=f"go version go{EXPECTED_VERSION}.1 darwin/arm64",
env_output="/valid/patch_binary",
)
assert get_goroot(rule_runner, [("go", valid_with_patch)]).path == "/valid/patch_binary"

# Should still work even if there are other Go versions with an invalid version.
invalid_version = mock_go_binary(
version_output="go version go1.8 darwin/arm64", env_output="/not/valid"
)
assert (
get_goroot(rule_runner, [("go", valid_without_patch), ("go", invalid_version)]).path
== "/valid/binary"
)

# Order of entries matters.
assert (
get_goroot(rule_runner, [("go", valid_without_patch), ("go", valid_with_patch)]).path
== "/valid/binary"
)
assert (
get_goroot(rule_runner, [("go", valid_with_patch), ("go", valid_without_patch)]).path
== "/valid/patch_binary"
)


def test_no_binaries(rule_runner: RuleRunner) -> None:
with pytest.raises(ExecutionError) as e:
get_goroot(rule_runner, [("not-go", "")])
exc = e.value.wrapped_exceptions[0]
assert isinstance(exc, BinaryNotFoundError)
assert "Cannot find any `go` binaries" in str(exc)


def test_no_valid_versions(rule_runner: RuleRunner) -> None:
invalid1 = mock_go_binary(
version_output="go version go1.8 darwin/arm64", env_output="/not/valid1"
)
invalid2 = mock_go_binary(
version_output="go version go1.8 darwin/arm64", env_output="/not/valid2"
)
with pytest.raises(ExecutionError) as e:
get_goroot(rule_runner, [("go", invalid1), ("go", invalid2)])
exc = e.value.wrapped_exceptions[0]
assert isinstance(exc, BinaryNotFoundError)
assert "Cannot find a `go` binary with the expected version" in str(exc)