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

pantsbuild / pants / 22361640215

24 Feb 2026 05:09PM UTC coverage: 92.342% (-0.6%) from 92.935%
22361640215

Pull #23133

github

web-flow
Merge fd48a7577 into 4d038bd74
Pull Request #23133: Add buildctl engine

194 of 282 new or added lines in 10 files covered. (68.79%)

452 existing lines in 24 files now uncovered.

89670 of 97106 relevant lines covered (92.34%)

4.01 hits per line

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

92.21
/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
        use_buildx=False,
65
        extra_args=("--pull", "--squash"),
66
    )
67

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

91

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

102

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

116

117
@pytest.mark.parametrize("podman_enabled", [True, False])
1✔
118
@pytest.mark.parametrize("podman_found", [True, False])
1✔
119
def test_get_docker(rule_runner: RuleRunner, podman_enabled: bool, podman_found: bool) -> None:
1✔
120
    docker_options = create_subsystem(
1✔
121
        DockerOptions,
122
        experimental_enable_podman=podman_enabled,
123
        tools=[],
124
        optional_tools=[],
125
    )
126
    docker_options_env_aware = mock.MagicMock(spec=DockerOptions.EnvironmentAware)
1✔
127

128
    def mock_find_binary(request: BinaryPathRequest) -> BinaryPaths:
1✔
129
        if request.binary_name == "podman" and podman_found:
1✔
UNCOV
130
            return BinaryPaths("podman", paths=[BinaryPath("/bin/podman")])
×
131

132
        elif request.binary_name == "docker":
1✔
133
            return BinaryPaths("docker", [BinaryPath("/bin/docker")])
1✔
134

135
        else:
UNCOV
136
            return BinaryPaths(request.binary_name, ())
×
137

138
    def mock_create_binary_shims(request: BinaryShimsRequest) -> BinaryShims:
1✔
139
        return BinaryShims(EMPTY_DIGEST, "cache_name")
×
140

141
    result = run_rule_with_mocks(
1✔
142
        get_docker,
143
        rule_args=[docker_options, docker_options_env_aware],
144
        mock_calls={
145
            "pants.core.util_rules.system_binaries.find_binary": mock_find_binary,
146
            "pants.core.util_rules.system_binaries.create_binary_shims": mock_create_binary_shims,
147
        },
148
    )
149

150
    if podman_enabled and podman_found:
1✔
151
        assert result.path == "/bin/podman"
1✔
UNCOV
152
        assert result.is_podman
×
153
    else:
154
        assert result.path == "/bin/docker"
1✔
155
        assert not result.is_podman
1✔
156

157

158
def test_get_docker_with_tools(rule_runner: RuleRunner) -> None:
1✔
159
    def mock_find_binary(request: BinaryPathRequest) -> BinaryPaths:
1✔
160
        if request.binary_name == "docker":
1✔
161
            return BinaryPaths("docker", paths=[BinaryPath("/bin/docker")])
1✔
162
        elif request.binary_name == "real-tool":
1✔
163
            return BinaryPaths("real-tool", paths=[BinaryPath("/bin/a-real-tool")])
1✔
164
        else:
165
            return BinaryPaths(request.binary_name, ())
1✔
166

167
    def mock_create_binary_shims(request: BinaryShimsRequest) -> BinaryShims:
1✔
168
        return BinaryShims(EMPTY_DIGEST, "cache_name")
1✔
169

170
    def run(tools: list[str], optional_tools: list[str]) -> None:
1✔
171
        docker_options = create_subsystem(
1✔
172
            DockerOptions,
173
            experimental_enable_podman=False,
174
            tools=tools,
175
            optional_tools=optional_tools,
176
        )
177
        docker_options_env_aware = mock.MagicMock(spec=DockerOptions.EnvironmentAware)
1✔
178

179
        nonlocal mock_find_binary
180
        nonlocal mock_create_binary_shims
181

182
        run_rule_with_mocks(
1✔
183
            get_docker,
184
            rule_args=[docker_options, docker_options_env_aware],
185
            mock_calls={
186
                "pants.core.util_rules.system_binaries.find_binary": mock_find_binary,
187
                "pants.core.util_rules.system_binaries.create_binary_shims": mock_create_binary_shims,
188
            },
189
        )
190

191
    run(tools=["real-tool"], optional_tools=[])
1✔
192

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

196
    # Optional non-existent tool should still succeed.
197
    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