Skip to content

Commit

Permalink
support debug mode for experimental_test_shell_command target type
Browse files Browse the repository at this point in the history
  • Loading branch information
tdyas committed Feb 14, 2025
1 parent dd87b85 commit eb13cc5
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
3 changes: 3 additions & 0 deletions docs/notes/2.26.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Some deprecations have expired and been removed:

The default version of the [Pex](https://docs.pex-tool.org/) tool has been updated from 2.32.0 to [2.33.0](https://github.com/pex-tool/pex/releases/tag/v2.33.0). Among many improvements and bug fixes, this unlocks support for pip [25.0.1](https://pip.pypa.io/en/stable/news/#v25-0-1).

#### Shell

The `experiemental_test_shell_command` target type may now be used with the `test` goal's `--debug` flag to execute the test interactively.

#### Terraform

Expand Down
36 changes: 35 additions & 1 deletion src/python/pants/backend/shell/goals/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@
)
from pants.backend.shell.util_rules import shell_command
from pants.backend.shell.util_rules.shell_command import ShellCommandProcessFromTargetRequest
from pants.core.goals.test import TestExtraEnv, TestFieldSet, TestRequest, TestResult, TestSubsystem
from pants.core.goals.test import (
TestDebugRequest,
TestExtraEnv,
TestFieldSet,
TestRequest,
TestResult,
TestSubsystem,
)
from pants.core.util_rules.environments import EnvironmentField
from pants.engine.internals.selectors import Get
from pants.engine.process import (
InteractiveProcess,
Process,
ProcessCacheScope,
ProcessResultWithRetries,
Expand Down Expand Up @@ -46,6 +54,7 @@ def opt_out(cls, tgt: Target) -> bool:
class ShellTestRequest(TestRequest):
tool_subsystem = ShellTestSubsystem
field_set_type = TestShellCommandFieldSet
supports_debug = True


@rule(desc="Test with shell command", level=LogLevel.DEBUG)
Expand Down Expand Up @@ -88,6 +97,31 @@ async def test_shell_command(
)


@rule(desc="Test with shell command (interactively)", level=LogLevel.DEBUG)
async def test_shell_command_interactively(
batch: ShellTestRequest.Batch[TestShellCommandFieldSet, Any],
) -> TestDebugRequest:
field_set = batch.single_element
wrapped_tgt = await Get(
WrappedTarget,
WrappedTargetRequest(field_set.address, description_of_origin="<infallible>"),
)

shell_process = await Get(
Process,
ShellCommandProcessFromTargetRequest(wrapped_tgt.target),
)

# This is probably not strictly necessary given the use of `InteractiveProcess` but good to be correct in any event.
shell_process = dataclasses.replace(shell_process, cache_scope=ProcessCacheScope.PER_SESSION)

return TestDebugRequest(
InteractiveProcess.from_process(
shell_process, forward_signals_to_process=False, restartable=True
)
)


def rules():
return (
*collect_rules(),
Expand Down
24 changes: 18 additions & 6 deletions src/python/pants/backend/shell/goals/test_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
)
from pants.build_graph.address import Address
from pants.core.goals import package
from pants.core.goals.test import TestResult, get_filtered_environment
from pants.core.goals.test import TestDebugRequest, TestResult, get_filtered_environment
from pants.core.util_rules import archive, source_files
from pants.engine.rules import QueryRule
from pants.engine.target import Target
from pants.testutil.rule_runner import RuleRunner
from pants.testutil.rule_runner import RuleRunner, mock_console

ATTEMPTS_DEFAULT_OPTION = 2

Expand All @@ -36,6 +36,7 @@ def rule_runner() -> RuleRunner:
*package.rules(),
get_filtered_environment,
QueryRule(TestResult, (ShellTestRequest.Batch,)),
QueryRule(TestDebugRequest, [ShellTestRequest.Batch]),
],
target_types=[
ShellSourcesGeneratorTarget,
Expand Down Expand Up @@ -95,11 +96,11 @@ def test_shell_command_as_test(rule_runner: RuleRunner) -> None:
)
(Path(rule_runner.build_root) / "test.sh").chmod(0o555)

def test_batch_for_target(test_target: Target) -> ShellTestRequest.Batch:
return ShellTestRequest.Batch("", (TestShellCommandFieldSet.create(test_target),), None)

def run_test(test_target: Target) -> TestResult:
input: ShellTestRequest.Batch = ShellTestRequest.Batch(
"", (TestShellCommandFieldSet.create(test_target),), None
)
return rule_runner.request(TestResult, [input])
return rule_runner.request(TestResult, [test_batch_for_target(test_target)])

pass_target = rule_runner.get_target(Address("", target_name="pass"))
pass_result = run_test(pass_target)
Expand All @@ -111,3 +112,14 @@ def run_test(test_target: Target) -> TestResult:
assert fail_result.exit_code == 1
assert fail_result.stdout_bytes == b"does not contain 'xyzzy'\n"
assert len(fail_result.process_results) == ATTEMPTS_DEFAULT_OPTION

# Check whether interactive execution via the `test` goal's `--debug` flags succeeds.
pass_debug_request = rule_runner.request(TestDebugRequest, [test_batch_for_target(pass_target)])
with mock_console(rule_runner.options_bootstrapper):
pass_debug_result = rule_runner.run_interactive_process(pass_debug_request.process)
assert pass_debug_result.exit_code == 0

fail_debug_request = rule_runner.request(TestDebugRequest, [test_batch_for_target(pass_target)])
with mock_console(rule_runner.options_bootstrapper):
fail_debug_result = rule_runner.run_interactive_process(fail_debug_request.process)
assert fail_debug_result.exit_code == 0

0 comments on commit eb13cc5

Please sign in to comment.