• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 23559966294

25 Mar 2026 07:27PM UTC coverage: 92.882% (-0.04%) from 92.918%
23559966294

Pull #23133

github

web-flow
Merge b6f2381e0 into 9b3c1562e
Pull Request #23133: Add buildctl engine

289 of 337 new or added lines in 13 files covered. (85.76%)

2 existing lines in 2 files now uncovered.

91640 of 98663 relevant lines covered (92.88%)

4.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.14
/src/python/pants/backend/docker/util_rules/docker_binary_test.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from hashlib import sha256
1✔
5
from unittest import mock
1✔
6

7
import pytest
1✔
8

9
from pants.backend.docker.subsystems.docker_options import DockerOptions
1✔
10
from pants.backend.docker.util_rules.binaries import DockerBinary, get_docker, rules
1✔
11
from pants.backend.docker.util_rules.docker_build_args import DockerBuildArgs
1✔
12
from pants.backend.experimental.docker.podman.register import rules as podman_rules
1✔
13
from pants.core.util_rules.system_binaries import (
1✔
14
    BinaryNotFoundError,
15
    BinaryPath,
16
    BinaryPathRequest,
17
    BinaryPaths,
18
    BinaryShims,
19
    BinaryShimsRequest,
20
)
21
from pants.engine.fs import EMPTY_DIGEST, Digest, DigestEntries
1✔
22
from pants.engine.process import Process, ProcessCacheScope
1✔
23
from pants.engine.rules import QueryRule
1✔
24
from pants.testutil.option_util import create_subsystem
1✔
25
from pants.testutil.rule_runner import RuleRunner, run_rule_with_mocks
1✔
26

27

28
@pytest.fixture
1✔
29
def docker_path() -> str:
1✔
30
    return "/bin/docker"
1✔
31

32

33
@pytest.fixture
1✔
34
def docker(docker_path: str) -> DockerBinary:
1✔
35
    return DockerBinary(docker_path)
1✔
36

37

38
@pytest.fixture
1✔
39
def rule_runner() -> RuleRunner:
1✔
40
    return RuleRunner(
1✔
41
        rules=[
42
            *rules(),
43
            *podman_rules(),
44
            QueryRule(DigestEntries, (Digest,)),
45
        ]
46
    )
47

48

49
def test_docker_binary_build_image(docker_path: str, docker: DockerBinary) -> None:
1✔
50
    dockerfile = "src/test/repo/Dockerfile"
1✔
51
    digest = Digest(sha256().hexdigest(), 123)
1✔
52
    tags = (
1✔
53
        "test:0.1.0",
54
        "test:latest",
55
    )
56
    env = {"DOCKER_HOST": "tcp://127.0.0.1:1234"}
1✔
57
    build_request = docker.build_image(
1✔
58
        tags=tags,
59
        digest=digest,
60
        dockerfile=dockerfile,
61
        build_args=DockerBuildArgs.from_strings("arg1=2"),
62
        context_root="build/context",
63
        env=env,
64
        extra_args=("--pull", "--squash"),
65
    )
66

67
    assert build_request == Process(
1✔
68
        argv=(
69
            docker_path,
70
            "build",
71
            "--pull",
72
            "--squash",
73
            "--tag",
74
            tags[0],
75
            "--tag",
76
            tags[1],
77
            "--build-arg",
78
            "arg1=2",
79
            "--file",
80
            dockerfile,
81
            "build/context",
82
        ),
83
        env=env,
84
        input_digest=digest,
85
        cache_scope=ProcessCacheScope.PER_SESSION,
86
        description="",  # The description field is marked `compare=False`
87
    )
88
    assert build_request.description == "Building docker image test:0.1.0 +1 additional tag."
1✔
89

90

91
def test_docker_binary_push_image(docker_path: str, docker: DockerBinary) -> None:
1✔
92
    image_ref = "registry/repo/name:tag"
1✔
93
    push_request = docker.push_image(image_ref)
1✔
94
    assert push_request == Process(
1✔
95
        argv=(docker_path, "push", image_ref),
96
        cache_scope=ProcessCacheScope.PER_SESSION,
97
        description="",  # The description field is marked `compare=False`
98
    )
99
    assert push_request.description == f"Pushing docker image {image_ref}"
1✔
100

101

102
def test_docker_binary_run_image(docker_path: str, docker: DockerBinary) -> None:
1✔
103
    image_ref = "registry/repo/name:tag"
1✔
104
    port_spec = "127.0.0.1:80:8080/tcp"
1✔
105
    run_request = docker.run_image(
1✔
106
        image_ref, docker_run_args=("-p", port_spec), image_args=("test-input",)
107
    )
108
    assert run_request == Process(
1✔
109
        argv=(docker_path, "run", "-p", port_spec, image_ref, "test-input"),
110
        cache_scope=ProcessCacheScope.PER_SESSION,
111
        description="",  # The description field is marked `compare=False`
112
    )
113
    assert run_request.description == f"Running docker image {image_ref}"
1✔
114

115

116
def test_get_docker(rule_runner: RuleRunner) -> None:
1✔
117
    docker_options = create_subsystem(
1✔
118
        DockerOptions,
119
        tools=[],
120
        optional_tools=[],
121
    )
122
    docker_options_env_aware = mock.MagicMock(spec=DockerOptions.EnvironmentAware)
1✔
123

124
    def mock_find_binary(request: BinaryPathRequest) -> BinaryPaths:
1✔
125
        if request.binary_name == "docker":
1✔
126
            return BinaryPaths("docker", [BinaryPath("/bin/docker")])
1✔
NEW
127
        return BinaryPaths(request.binary_name, ())
×
128

129
    def mock_create_binary_shims(request: BinaryShimsRequest) -> BinaryShims:
1✔
130
        return BinaryShims(EMPTY_DIGEST, "cache_name")
×
131

132
    result = run_rule_with_mocks(
1✔
133
        get_docker,
134
        rule_args=[docker_options, docker_options_env_aware],
135
        mock_calls={
136
            "pants.core.util_rules.system_binaries.find_binary": mock_find_binary,
137
            "pants.core.util_rules.system_binaries.create_binary_shims": mock_create_binary_shims,
138
        },
139
    )
140

141
    assert isinstance(result, DockerBinary)
1✔
142
    assert result.path == "/bin/docker"
1✔
143

144

145
def test_get_docker_with_tools(rule_runner: RuleRunner) -> None:
1✔
146
    def mock_find_binary(request: BinaryPathRequest) -> BinaryPaths:
1✔
147
        if request.binary_name == "docker":
1✔
148
            return BinaryPaths("docker", paths=[BinaryPath("/bin/docker")])
1✔
149
        elif request.binary_name == "real-tool":
1✔
150
            return BinaryPaths("real-tool", paths=[BinaryPath("/bin/a-real-tool")])
1✔
151
        else:
152
            return BinaryPaths(request.binary_name, ())
1✔
153

154
    def mock_create_binary_shims(request: BinaryShimsRequest) -> BinaryShims:
1✔
155
        return BinaryShims(EMPTY_DIGEST, "cache_name")
1✔
156

157
    def run(tools: list[str], optional_tools: list[str]) -> None:
1✔
158
        docker_options = create_subsystem(
1✔
159
            DockerOptions,
160
            tools=tools,
161
            optional_tools=optional_tools,
162
        )
163
        docker_options_env_aware = mock.MagicMock(spec=DockerOptions.EnvironmentAware)
1✔
164

165
        nonlocal mock_find_binary
166
        nonlocal mock_create_binary_shims
167

168
        run_rule_with_mocks(
1✔
169
            get_docker,
170
            rule_args=[docker_options, docker_options_env_aware],
171
            mock_calls={
172
                "pants.core.util_rules.system_binaries.find_binary": mock_find_binary,
173
                "pants.core.util_rules.system_binaries.create_binary_shims": mock_create_binary_shims,
174
            },
175
        )
176

177
    run(tools=["real-tool"], optional_tools=[])
1✔
178

179
    with pytest.raises(BinaryNotFoundError, match="Cannot find `nonexistent-tool`"):
1✔
180
        run(tools=["real-tool", "nonexistent-tool"], optional_tools=[])
1✔
181

182
    # Optional non-existent tool should still succeed.
183
    run(tools=[], optional_tools=["real-tool", "nonexistent-tool"])
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc