Skip to content

Conversation

@harvey0100
Copy link
Contributor

@harvey0100 harvey0100 commented Nov 3, 2025

Summary by CodeRabbit

  • Bug Fixes

    • More accurate process existence and kill behavior (distinguish ESRCH vs permission, respect ownership and optionally use privileged kill), timeout handling for killing process trees, stricter binary extraction errors, and clearer command-result decoding behavior.
  • Documentation

    • Expanded docs for sudo checks, capability queries (can target specific processes), procfs parent/child discovery, subprocess helpers, and command-output behaviors.
  • Tests

    • Added extensive unit and functional tests for environment propagation, large/Unicode output, concurrent subprocesses, encoding, and command parsing.
  • Chores

    • Updated spell-ignore patterns and lint ignore list.

✏️ Tip: You can customize this high-level summary in your review settings.

@mr-avocado mr-avocado bot moved this to Review Requested in Default project Nov 3, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR expands and clarifies docstrings across avocado/utils/process.py, updates has_capability to accept an optional pid, makes binary_from_shell_cmd raise ValueError when no binary is found, clarifies pid_exists behavior (ESRCH → False) and safe_kill semantics (owner checks and sudo path), and documents other function behaviors. It adds extensive unit and functional tests for process utilities, updates test counts in selftests/check.py, appends entries to spell.ignore, and reorders the .pylintrc_utils blacklist.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • avocado/utils/process.py: signature change for has_capability, binary_from_shell_cmd ValueError behavior, pid_exists and safe_kill edge cases, get_owner_id return-on-failure, and other docstring-to-behavior consistency.
    • New and modified tests for correctness, determinism, and flakiness:
      • selftests/unit/utils/process.py
      • selftests/functional/utils/process.py (concurrency, large-output, encoding)
    • Test count updates in selftests/check.py.
    • spell.ignore additions and .pylintrc_utils blacklist reordering.

Pre-merge checks

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Process Module Preperation' is vague and generic; it lacks specificity about what changes are being made to the process module, making it unclear to reviewers scanning history. Replace with a more specific title that describes the primary change, such as 'Add documentation and tests for process module utilities' or 'Expand docstrings and unit tests in process module'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 85.90% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@harvey0100 harvey0100 changed the title Process Modle Preperation Process Module Preperation Nov 3, 2025
@harvey0100 harvey0100 self-assigned this Nov 3, 2025
@harvey0100 harvey0100 requested a review from richtja November 3, 2025 16:18
@codecov
Copy link

codecov bot commented Nov 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.59%. Comparing base (4ff9e2c) to head (0663962).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6229      +/-   ##
==========================================
+ Coverage   73.50%   73.59%   +0.08%     
==========================================
  Files         206      206              
  Lines       22497    22497              
==========================================
+ Hits        16536    16556      +20     
+ Misses       5961     5941      -20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@clebergnu clebergnu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @harvey0100 ,

Thanks for this, it's a great improvement. Please take a look at a few comments I left.

Copy link
Contributor

@richtja richtja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @harvey0100, thank you for preparing the process module for the migration. I have few comments to the tests, and I am also missing the update of pylint checks, which would reveal any issues with the docstring changes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
avocado/utils/process.py (2)

65-101: Fix bytes/str comparison in can_sudo() for non‑root check

In the cmd is None branch you do:

if system_output("id -u", ignore_status=True, sudo=True).strip() == "0":
    return True

system_output() returns bytes, so this comparison against the string "0" will always be False. That means can_sudo() will incorrectly return False for non‑root users with a working sudo configuration when no cmd is passed.

A minimal fix is to compare against a bytes literal:

-        if system_output("id -u", ignore_status=True, sudo=True).strip() == "0":
+        if system_output("id -u", ignore_status=True, sudo=True).strip() == b"0":
             return True

Optionally, you may also want a unit test that covers can_sudo() (no cmd) for non‑root with functional sudo to guard this behavior.


379-404: Fix bytes/str mismatch in process_in_ptree_is_defunct()

Here:

proc_name = system_output(cmd, ignore_status=True, verbose=False)
if "<defunct>" in proc_name:
    defunct = True
    break

system_output() returns bytes, so checking for a text substring raises a TypeError on Python 3 ('in <bytes>' requires a bytes-like object). This makes the function unreliable whenever it actually inspects proc_name.

A minimal fix is to compare using a bytes literal:

-        proc_name = system_output(cmd, ignore_status=True, verbose=False)
-        if "<defunct>" in proc_name:
+        proc_name = system_output(cmd, ignore_status=True, verbose=False)
+        if b"<defunct>" in proc_name:
             defunct = True
             break

Alternatively, you could decode the output once and stay in the text domain, but the bytes‑literal change is smallest and consistent with system_output()’s API.

🧹 Nitpick comments (1)
selftests/unit/utils/process.py (1)

545-578: can_sudo tests are good, but one important scenario is untested

The new tests nicely cover root, missing sudo, specific command success, and OSError cases. What’s missing is a test for the common path can_sudo() as a non‑root user with a working sudo configuration (no cmd argument), which would exercise the system_output("id -u", ...) branch. That missing test would also have caught the current bytes/str comparison bug in can_sudo() (see comment on avocado/utils/process.py around Line 65).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5207e5f and 2b39777.

📒 Files selected for processing (6)
  • .pylintrc_utils (1 hunks)
  • avocado/utils/process.py (43 hunks)
  • selftests/check.py (1 hunks)
  • selftests/functional/utils/process.py (3 hunks)
  • selftests/unit/utils/process.py (2 hunks)
  • spell.ignore (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • spell.ignore
  • selftests/check.py
🧰 Additional context used
🧬 Code graph analysis (1)
selftests/functional/utils/process.py (1)
avocado/utils/process.py (5)
  • system_output (1216-1284)
  • getstatusoutput (1350-1413)
  • SubProcess (653-1071)
  • stdout_text (505-516)
  • binary_from_shell_cmd (407-435)
🪛 Ruff (0.14.7)
selftests/unit/utils/process.py

644-644: Function call with shell=True parameter identified, security issue

(S604)

🔇 Additional comments (33)
.pylintrc_utils (1)

10-10: LGTM: Cosmetic reordering with no functional impact.

This reordering of the ignore list has no effect on pylint's behavior, as the list is treated as a set of files to skip.

selftests/unit/utils/process.py (5)

359-365: binary_from_shell_cmd invalid‑input coverage looks correct

The new test cases exercise both the “env‑only” command and empty string paths, matching the updated behavior of raising ValueError when no binary can be parsed.


579-597: has_capability unit tests match the API, including pid usage

These tests validate both positive/negative capability membership and confirm that passing pid propagates correctly to get_capabilities(pid). They align well with the updated docstring and implementation.


598-620: pid_exists tests correctly encode ESRCH vs EPERM semantics

The three tests cover success, non‑existent PID (errno.ESRCH → False) and permission‑denied (errno.EPERM → True), which matches the intended behavior of pid_exists(). This is a solid regression‑proofing of the subtle errno handling.


621-634: kill_process_by_pattern tests validate pkill wiring and failure handling

The success test asserts the exact run("pkill -f pattern", ignore_status=True) invocation, and the failure test ensures that a non‑zero exit status doesn’t raise. That matches the implementation, which only logs on failure.


635-685: system/system_output/getoutput/getstatusoutput integration tests look appropriate

These tests exercise:

  • system() success and non‑zero exit behavior,
  • system_output() return type (bytes) and newline‑stripping behavior,
  • getoutput() string output,
  • getstatusoutput() returning (status, output) and handling failures.

They align with the documented and implemented behavior of the wrappers. The shell=True usage in the failure tests is acceptable here because the commands are fixed literals and this is a controlled test environment.

avocado/utils/process.py (19)

17-17: Module‑level docstring is a clear improvement

The new module docstring succinctly describes the purpose of this module (“find and run external commands”) and improves discoverability without changing behavior.


104-134: get_capabilities/has_capability doc updates and pid support look consistent

The get_capabilities() and has_capability() docstrings now clearly explain pid handling, capability naming, and return types, and the has_capability(capability, pid=None) implementation simply delegates to get_capabilities(pid). The behavior matches both the docs and the new unit tests.

Also applies to: 136-161


164-187: pid_exists semantics and documentation align with tests

The expanded docstring accurately documents the os.kill(pid, 0) technique and the ESRCH handling. The logic (False on errno.ESRCH, True otherwise, including EPERM) is consistent with both standard practice and the new unit tests.


190-221: safe_kill owner‑based logic matches tests and intent

Using get_owner_id(int(pid)) to decide between a sudo‑based kill (owner is root or unknown) and direct os.kill() (non‑root) matches the new unit tests and the goal of handling privilege requirements transparently. The boolean return based on success/failure is also clearly documented now.


223-244: get_parent_pid Linux‑specific docstring is accurate

The new docstring (including the Linux‑specific note) correctly describes the /proc/<pid>/stat‑based implementation and the return type. Behavior is unchanged and still matches existing tests.


253-291: get_children_pids docs now reflect actual behavior, including recursion

The docstring now clearly states that this is /proc‑based, Linux‑specific, and explains the recursive flag semantics, return type, and example usage. This is in line with the implementation and the existing Linux‑only unit test.


294-353: kill_process_tree documentation is clearer and consistent with implementation

The updated docstring accurately documents sig, send_sigcont, timeout semantics, and the RuntimeError behavior when processes don’t die within the timeout. The implementation is unchanged and already well covered by unit tests.


355-377: kill_process_by_pattern docstring matches pkill‑based implementation

The docstring now makes explicit that pkill -f is used, that matching is against the full command line, and that the operation logs success/failure but doesn’t raise. This lines up with the implementation and the new unit tests.


407-435: binary_from_shell_cmd behavior and docs are now well‑specified

The enhanced docstring (including examples and the ValueError contract) aligns with the implementation that skips env assignments via _RE_BASH_SET_VARIABLE and returns the first non‑assignment token. The new unit and functional tests cover both valid and invalid inputs, including env‑only commands and empty strings.


444-463: CmdResult documentation and text helpers are clear and match usage

The expanded class docstring and the stdout_text/stderr_text property docs correctly describe the bytes→text decoding behavior and the TypeError raised when decoding is impossible. This matches how the tests exercise CmdResult and how higher‑level helpers like getstatusoutput() rely on stdout_text.

Also applies to: 504-531


533-577: FDDrainer and its helpers are now better documented

The FDDrainer class and its methods (__init__, _drainer, start, flush) are now clearly documented, including the roles of logger, stream_logger, ignore_bg_processes, and verbose. The implementation remains unchanged and is well covered by the FDDrainer tests.

Also applies to: 596-625


653-702: SubProcess API docstrings are now comprehensive and aligned with behavior

The updated docstrings for SubProcess and its methods (__init__, _fill_streams, start, get_stdout, get_stderr, terminate, kill, send_signal, poll, wait, stop, get_pid, get_user_id, is_sudo_enabled, run) clearly document parameters, return types, and error conditions (including the zombie AssertionError). They line up with the existing implementation and the unit/functional tests that exercise timeouts, sudo, and signal handling.

Also applies to: 828-872, 885-901, 903-949, 1002-1017, 1023-1049, 1050-1071


1075-1145: run(): error contracts and return type are now explicit

The docstring now documents CmdInputError for empty commands and CmdError when ignore_status=False and the subprocess fails, plus the CmdResult return type. This matches the implementation (if not cmd: raise CmdInputError(...)) and the new test_empty_command unit test.


1149-1213: system() wrapper docstring correctly describes the API

The updated docstring explains that system() forwards to run() and returns only the exit code while propagating CmdError when ignore_status=False. This behavior is consistent with both the implementation and the unit tests for system().


1216-1285: system_output(): bytes return type and behavior are clearly specified

The docstring now makes explicit that system_output() returns bytes, supports strip_trail_nl, and can raise CmdError. This matches the implementation and the new unit tests that assert on raw bytes and newline behavior.


1288-1335: getoutput() documentation matches its getstatusoutput‑based implementation

The docstring correctly states that it returns a str, mirrors commands.getoutput() semantics, and forwards to getstatusoutput() with shell=True and ignore_status=True by default. That matches the actual implementation and new integration tests.


1350-1397: getstatusoutput() docs align with wrapper behavior

The function is now documented as returning (status, output) where output is the decoded stdout text with a trailing newline removed. This matches the current implementation and the new tests that assert on both status and string output.


1416-1439: get_owner_id(): Linux note and None‑on‑failure behavior are clear

The docstring now documents that this is /proc‑based, Linux‑specific, and returns None when the process cannot be found or read. That matches the implementation (os.stat(...).st_uid with OSErrorNone) and the new tests that cover both success and failure paths.


1442-1461: get_command_output_matching docstring matches substring‑per‑line implementation

The clarified docs (line‑by‑line substring matching, list of matching lines) exactly reflect the comprehension [...] if pattern in line applied to run(command).stdout_text.splitlines(). This is simple and consistent with expectations.

selftests/functional/utils/process.py (8)

125-143: Script templates centralize test scripts and improve readability

Defining ENV_TEST_SCRIPT, SUCCESS_SCRIPT, LARGE_OUTPUT_SCRIPT, and UNICODE_TEST_SCRIPT as format‑based templates (with {interpreter}) removes duplication and makes the functional tests easier to read and maintain. The use of {{i}} in LARGE_OUTPUT_SCRIPT is correct for later .format() substitution.


158-169: _create_script() helper nicely factors out script creation logic

This helper cleanly encapsulates script creation with interpreter substitution, UTF‑8 encoding, and executable permissions under tmpdir. It directly addresses previous feedback about repeating script‑creation boilerplate.


215-229: Environment propagation tests for run() and system_output() are solid

test_run_and_system_output_with_environment verifies that both process.run(..., env=...) and process.system_output(..., env=...) honor custom environment variables. The assertions on stdout content and exit status are strict enough to catch regressions.


230-237: getstatusoutput integration test validates real‑world usage

test_getstatusoutput_integration exercises process.getstatusoutput() against an actual script, confirming both status==0 and string output "success". This nicely complements the unit tests that use echo.


238-253: Multiple concurrent SubProcess instances test is appropriate

test_multiple_subprocess_instances starts three SubProcess instances that sleep for 1s and print their index, then waits on each and asserts exit_status == 0 and that the corresponding index appears in stdout. This provides a good smoke test of concurrent subprocess management without relying on ordering.


254-271: Large output test now strictly validates completeness and integrity

test_process_with_large_output checks that:

  • the command exits with 0,
  • exactly 1000 lines are captured, and
  • the first and last lines have the expected repeated pattern.

This directly addresses concerns about partial capture of large stdout and ensures detection of truncation/corruption.


272-279: CmdResult encoding behavior is functionally validated

test_cmdresult_encoding confirms that encoding="utf-8" is preserved on the result and that stdout_text correctly decodes Unicode output ("Avokádo"). This is a good end‑to‑end check of the encoding plumbing.


280-287: binary_from_shell_cmd_realworld test covers realistic command forms

The “real‑world” patterns (env assignments before /usr/bin/python, and sudo -u user /bin/bash -c ...) validate that binary_from_shell_cmd() returns the expected first executable (/usr/bin/python and sudo respectively), complementing the more synthetic unit tests.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (6)
avocado/utils/process.py (3)

136-162: has_capability() PID support and man‑page note are clear

The added pid parameter and the note about UPPERCASE names in capabilities(7) versus lowercase Python strings address the earlier review concern and make the API easier to use. Implementation simply delegates to get_capabilities(pid), which is appropriate.


223-244: get_parent_pid() doc now correctly marks Linux‑specific behavior

The added docstring (including the Linux‑specific note) matches the implementation that reads /proc/<pid>/stat and extracts the PPID. Looks good.


1416-1439: get_owner_id() doc now calls out Linux /proc dependency

The new docstring (including the Linux note and None on error) matches the implementation using os.stat("/proc/<pid>/"). This also complements the safe_kill / SubProcess sudo logic.

selftests/functional/utils/process.py (3)

125-142: Script templates parameterized by {interpreter} look correct

The new ENV_TEST_SCRIPT, SUCCESS_SCRIPT, LARGE_OUTPUT_SCRIPT, and UNICODE_TEST_SCRIPT templates centralize the script contents and make it easy to inject the right interpreter path via .format(interpreter=sys.executable). This addresses the earlier suggestion about using global constants for script bodies.


158-169: _create_script() nicely de‑duplicates script setup

The helper correctly:

  • Writes the formatted contents with the right interpreter.
  • Places scripts under self.tmpdir.
  • Applies DEFAULT_MODE so they are executable.

This reduces duplication across tests and matches the prior review suggestion to factor script creation into a shared method.


255-271: Large‑output test precisely validates capture completeness

test_process_with_large_output verifies:

  • Exit status 0.
  • Exactly 1000 lines captured.
  • Correct content of the first and last lines.

This is a strong regression guard for buffering/draining issues and addresses the earlier concern about being more strict on output size.

🧹 Nitpick comments (4)
avocado/utils/process.py (4)

190-221: Clarify ownership logic in safe_kill()

The behavior is reasonable, but the ownership check is slightly opaque:

  • if not get_owner_id(int(pid)): will treat both uid == 0 and uid is None as “needs sudo / elevated path”, leading to run("kill -…", sudo=True).
  • For non‑root owners (uid != 0), os.kill is used directly.

Functionally this works (root callers end up running plain kill because _prepend_sudo() is a no‑op for uid 0, and missing /proc entries are handled via CmdError), but readability would improve with an explicit branch such as:

-    if not get_owner_id(int(pid)):
+    owner_id = get_owner_id(int(pid))
+    if owner_id is None or owner_id == 0:

That also makes the “unknown owner vs. root‑owned” distinction clearer for future readers.


355-377: kill_process_by_pattern() logging fine, consider minor polish

Behavior is unchanged except for clearer docs and logging:

  • Uses pkill -f with ignore_status=True.
  • Logs an error on non‑zero exit status and info on success.

Two very minor nits you can take or leave:

  • The info message could read “Succeeded to run '%s'.” or “Successfully ran '%s'.” instead of “Succeed to run '%s'.” for grammar.
  • If you ever expect patterns with whitespace or shell metacharacters, cmd = f"pkill -f {pattern!r}" (and corresponding adjustment in tests) would make the pattern handling more robust.

504-517: stdout_text / stderr_text doc vs actual exceptions

The implementations correctly:

  • Decode bytes using self.encoding.
  • Return strings as‑is.
  • Raise TypeError when the stored value is neither bytes nor str.

Note that for undecodable bytes the underlying decode() will raise UnicodeDecodeError (a ValueError subclass), not TypeError. The docstrings currently only mention TypeError, which is accurate for the “wrong type” case but incomplete for the “invalid byte sequence” case.

If you want the docs to be fully precise, you could mention both UnicodeDecodeError and TypeError as possible failures here.


654-702: SubProcess.__init__ docs align with current behavior, with a minor nit

The constructor docs now clearly describe cmd, environment merging, sudo behavior, and logging. Implementation matches the description.

One small mismatch: the doc advertises :raises ValueError: If incorrect values are given to parameters., but the body does not currently raise a ValueError for any parameter combinations. If no such validation is planned, you might want to drop that :raises line to avoid misleading readers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b39777 and b97f0ca.

📒 Files selected for processing (6)
  • .pylintrc_utils (1 hunks)
  • avocado/utils/process.py (43 hunks)
  • selftests/check.py (1 hunks)
  • selftests/functional/utils/process.py (3 hunks)
  • selftests/unit/utils/process.py (2 hunks)
  • spell.ignore (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • spell.ignore
  • .pylintrc_utils
🧰 Additional context used
🧬 Code graph analysis (2)
selftests/functional/utils/process.py (2)
avocado/utils/process.py (5)
  • system_output (1216-1284)
  • getstatusoutput (1350-1413)
  • SubProcess (653-1071)
  • stdout_text (505-516)
  • binary_from_shell_cmd (407-435)
selftests/utils.py (1)
  • skipOnLevelsInferiorThan (133-138)
selftests/unit/utils/process.py (1)
avocado/utils/process.py (9)
  • binary_from_shell_cmd (407-435)
  • can_sudo (65-101)
  • has_capability (136-161)
  • pid_exists (164-187)
  • kill_process_by_pattern (355-376)
  • system (1149-1212)
  • system_output (1216-1284)
  • getoutput (1288-1346)
  • getstatusoutput (1350-1413)
🪛 Ruff (0.14.7)
selftests/unit/utils/process.py

644-644: Function call with shell=True parameter identified, security issue

(S604)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
  • GitHub Check: Linux with Python 3.12
  • GitHub Check: Linux with Python 3.10
  • GitHub Check: Linux with Python 3.9
  • GitHub Check: Linux with Python 3.11
  • GitHub Check: Linux with Python 3.13
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: Fedora selftests
  • GitHub Check: Egg task ubi:8.8
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Podman spawner with 3rd party runner plugin
  • GitHub Check: Windows with Python 3.13
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: macOS with Python 3.11
  • GitHub Check: Static checks
  • GitHub Check: Code Coverage (3.11)
🔇 Additional comments (36)
selftests/check.py (1)

32-32: Confirm updated functional-parallel TEST_SIZE matches new suite size

The bump to TEST_SIZE["functional-parallel"] = 348 looks consistent with the added functional tests, but this mapping is brittle. Please confirm you re-ran selftests/check.py (or equivalent listing) and verified that 348 matches the current functional suite size on a clean tree.

avocado/utils/process.py (24)

65-102: can_sudo() behavior with optional cmd looks sound

The new cmd parameter and logic are consistent with the docstring:

  • Root (uid 0) still short‑circuits to True.
  • Non‑root: presence of sudo is checked via path.find_command, and either a concrete cmd (via system(..., sudo=True)) or a simple id -u check is used.
  • Broken sudo (OSError) is safely treated as False.

Nothing blocking here; good balance between functionality and safety.


104-134: Capabilities parsing and Linux‑specific semantics look correct

get_capabilities(pid=None) now clearly documents PID handling and the two getpcaps formats, and the implementation matches the description. Returning an empty list on FileNotFoundError or non‑zero exit preserves the “no capabilities / no getpcaps” semantics.


165-187: pid_exists() ESRCH/EPERM semantics are correct but worth calling out

The new behavior of returning False only for errno.ESRCH and treating other errors (notably EPERM) as “PID exists” matches the common kill(pid, 0) pattern and makes this helper safer under restricted permissions. This is a sensible improvement and aligns with the docstring.


253-292: get_children_pids() recursive behavior matches docs

Docstring now clearly describes recursive vs non‑recursive modes and the Linux /proc dependency. Implementation (glob over /proc/*/stat, parse PPID, recurse on matching children) aligns with the description; no issues spotted.


294-353: kill_process_tree() timeout semantics remain consistent

The expanded docstring reflects the current behavior:

  • Default sig is SIGKILL when none is provided.
  • A preliminary SIGSTOP is sent via safe_kill.
  • Descendants are gathered via get_children_pids and killed recursively.
  • timeout > 0 uses wait_for plus pid_exists, and negative timeouts loop indefinitely until all PIDs disappear.

Given the updated pid_exists() semantics, this is internally consistent. No functional issues noticed.


379-405: process_in_ptree_is_defunct() docs now align with implementation

The updated docstring clarifies that this relies on GNU ps and may not work on macOS, which matches the ps --no-headers -o cmd invocation. Returning True on CmdError (missing parent) continues to treat missing processes as “defunct from the caller's perspective”, which is conservative but acceptable.


444-463: CmdResult docstring is clear and matches constructor usage

The parameter documentation (including encoding) lines up with how CmdResult is constructed throughout the module. Centralizing the meaning of encoding here is helpful given the broader process API.


533-577: FDDrainer docs and behavior match its usage

The refreshed FDDrainer docstring and parameter docs match how SubProcess wires it up. The _drainer() logic with ignore_bg_processes, buffering, and logging is unchanged and looks correct. No issues from a concurrency or resource‑handling standpoint.


844-853: _fill_streams() cleanup and result population are consistent

The docstring now explains that stdout/stderr pipes are closed and results populated. The call order (flush drainers, then read from their buffers into CmdResult) is correct and matches how the rest of SubProcess expects to consume data.


855-862: start() doc makes background‑process usage clearer

The added explanation that start() is useful for background processes and returns the PID is accurate and matches the behavior. No changes needed.


867-883: get_stdout() / get_stderr() docs consistent with bytes return type

The new docstrings emphasize that these methods return raw bytes, which is consistent with how CmdResult.stdout/stderr are set. Good clarification, especially given the existence of stdout_text / stderr_text.


885-901: terminate() and kill() docs encourage safer stop() usage

The expanded docstrings recommending stop() over direct terminate()/kill() are helpful and match how wait() handles timeouts and tree termination. Behavior remains unchanged and looks correct.


902-918: send_signal() sudo‑aware logic remains sensible

The new doc clarifies semantics; implementation:

  • Resolves the full tree via get_children_pids(self.get_pid()) when running as root/sudo and sends kill via run(..., sudo=True), suppressing errors.
  • Falls back to Popen.send_signal otherwise.

This is consistent with the higher‑level process‑tree management functions and with the new is_sudo_enabled() behavior.


920-947: poll() and wait() docs now reflect timeout/zombie handling

Docstrings correctly describe:

  • poll() returning None while the process is running and filling results once it finishes.
  • wait()’s behavior with timeout=None, timeout>0, and implicit 1‑second grace followed by tree nuking and potential AssertionError for zombies.

The underlying logic is complex but appears internally consistent and is well tested by the existing “refuse_to_die” tests.


1003-1021: stop() doc correctly describes graceful shutdown flow

stop()’s docstring now mirrors wait()’s timeout and signal logic and clarifies that it terminates and waits when the process is still running. Behavior is unchanged and appropriate.


1023-1049: get_pid() / get_user_id() / is_sudo_enabled() docs align with implementation

  • get_pid() returns the actual Popen.pid.
  • get_user_id() delegates to get_owner_id.
  • is_sudo_enabled() essentially checks for uid 0 (via get_user_id() returning falsy).

Docs match that behavior and make the sudo detection clearer to callers.


1051-1071: SubProcess.run() doc is consistent with wait() and result usage

The method correctly starts (if needed), waits with the given timeout/signal semantics, and returns the populated CmdResult. The type hints and description are accurate.


1075-1145: run() wrapper: empty‑command guard and docs look good

The updated run():

  • Validates cmd and raises CmdInputError on empty input (good defensive behavior).
  • Clearly documents all parameters, including environment, sudo, and ignore_bg_processes.
  • Returns a CmdResult and raises CmdError when ignore_status=False and the command fails.

The implementation matches the docstring; the new empty‑command check is also covered by unit tests.


1149-1199: system() wrapper docs and implementation are aligned

system() now explicitly documents its reliance on run() and that it returns just the exit code, raising CmdError when ignore_status=False. The code simply forwards parameters to run() and returns exit_status, which is correct.


1216-1269: system_output() docs clarify bytes return and newline handling

The docstring correctly states that:

  • The output is returned as bytes.
  • Trailing newlines are stripped when strip_trail_nl=True.

The implementation matches this behavior and simply wraps run(). No issues spotted.


1288-1335: getoutput() wrapper behavior matches legacy commands.getoutput

The docstring makes clear that stderr is redirected into stdout (via getstatusoutput), and the implementation simply indexes [1] from getstatusoutput. This is consistent with the Python 2 commands module semantics.


1350-1397: getstatusoutput() integration with run() looks correct

The function wraps run() with shell=True by default, then converts stdout to text and strips a final newline. That matches the documented return type ((status, output)) and compatibility goal with Python’s old commands module. No concerns here.


1442-1455: get_command_output_matching() doc accurately reflects behavior

The docstring now clearly states that matching is substring‑based on each output line, and that a list of matching lines is returned. Implementation using run(command).stdout_text.splitlines() is consistent with that.


407-436: binary_from_shell_cmd() raising ValueError is a behavior change

The new behavior of raising ValueError (instead of silently returning None) when no non‑assignment token is found is a good defensive choice and is well covered by unit tests. It will, however, break any external callers that previously relied on a falsey "no binary" result rather than handling an exception.

If this function is part of the public API (used by plugins or external consumers), consider:

  • Documenting the change in release notes / deprecation notes, or
  • Keeping a transitional behavior (e.g., optional raise_on_error=True flag) if you expect third‑party usage.

Inside Avocado itself this looks safe given the updated tests.

selftests/unit/utils/process.py (6)

359-365: test_binary_from_shell_cmd_invalid correctly asserts new error behavior

These tests explicitly validate that purely “VAR=VALUE …” inputs and the empty string now raise ValueError, which matches the new binary_from_shell_cmd() contract and helps catch regressions.


545-578: can_sudo() unit tests cover key branches

The added tests exercise:

  • Root short‑circuit (uid == 0).
  • No sudo installed (CmdNotFoundError).
  • Successful specific command via system(..., sudo=True).
  • Broken sudo path raising OSError.

This nicely covers the major control‑flow paths in can_sudo() and aligns with the implementation.


579-597: has_capability() tests validate both presence and PID forwarding

The tests confirm:

  • Positive and negative capability membership against mocked get_capabilities.
  • That the optional pid argument is forwarded correctly.

This accurately locks in the intended semantics of the wrapper.


598-620: pid_exists() tests nail ESRCH vs EPERM behavior

The unit tests distinguish between:

  • Success (os.kill returns normally).
  • Non‑existent PID (errno.ESRCH -> False).
  • Permission denied (errno.EPERM -> True).

This matches the new implementation and ensures the subtle semantics stay correct.


621-634: kill_process_by_pattern tests verify both success and failure paths

The tests check:

  • That run("pkill -f <pattern>", ignore_status=True) is invoked with the expected command.
  • That a non‑zero exit status does not raise.

This aligns with kill_process_by_pattern()’s implementation and logging behavior.


635-685: system / system_output / getoutput / getstatusoutput tests look good; S604 is a false positive here

These tests validate:

  • system() returning the exit code (0 for a simple echo, non‑zero for false with shell=True).
  • system_output() returning bytes and respecting strip_trail_nl.
  • getoutput() returning decoded text.
  • getstatusoutput() returning (status, text) for both success and failure.

The shell=True use in test_system_with_failure is over a constant string "false", so the Ruff S604 warning (“shell=True” security issue) is not a practical risk in this context. Keeping shell=True here is appropriate to exercise that specific code path.

selftests/functional/utils/process.py (5)

215-229: Env propagation tests for run() and system_output() are solid

test_run_and_system_output_with_environment verifies that both process.run() and process.system_output() respect the env dict, which is important given the env merging logic in SubProcess. Using the small helper script keeps this focused and robust.


230-237: test_getstatusoutput_integration exercises real command path

This test ensures that getstatusoutput() works end‑to‑end with an actual executable script and that the (status, output) contract holds. Good integration coverage on top of the unit tests.


238-253: test_multiple_subprocess_instances is a reasonable concurrency sanity check

Spawning three SubProcess instances that each sleep and print a unique index, then asserting exit status and output, is a good basic concurrency test. The 1‑second sleep is a pragmatic choice to avoid flakiness while still overlapping process lifetimes.


272-279: test_cmdresult_encoding confirms encoding propagation and decoding

By forcing encoding="utf-8" on run() and then asserting on both result.encoding and decoded stdout_text, this test gives practical coverage of the encoding behavior clarified in CmdResult. Looks good.


280-287: Real‑world binary_from_shell_cmd patterns are well covered

The tests for:

  • "FOO=bar BAR=baz /usr/bin/python script.py --arg value"/usr/bin/python
  • "sudo -u user /bin/bash -c 'echo test'"sudo

align with the intended behavior of binary_from_shell_cmd() and complement the more synthetic unit tests.

@harvey0100
Copy link
Contributor Author

Hi @richtja i have amended the commits towards your comments :)

@harvey0100
Copy link
Contributor Author

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly improves the avocado/utils/process.py module by adding comprehensive docstrings, type hints, and examples to most functions and methods. It also enhances correctness by fixing subtle bugs in process handling, such as in pid_exists and binary_from_shell_cmd. The test coverage is substantially increased with new unit and functional tests that cover environment variable propagation, large outputs, and concurrent subprocesses. Overall, this is an excellent contribution that greatly improves the maintainability and reliability of the process utility module. I have a couple of minor suggestions regarding import placement in the new tests.

Copy link
Contributor

@richtja richtja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @harvey0100, beside the Gemini comments it LGTM. Please fix those and resolve the conflicts so we could get the CI results. Thank you.

Process Module Improved RST Docstrings Module

Reference: avocado-framework/aautils#52
Assisted-By: Cursor-4-Sonnet
Signed-off-by: Harvey Lynden <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
spell.ignore (1)

820-820: substring allowlist seems fine, but consider whether you want to allow such a generic term.
If it’s only needed for a specific doc/test phrase, this is still acceptable; just be aware it may hide real typos elsewhere.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b39777 and 8ad3626.

📒 Files selected for processing (6)
  • .pylintrc_utils (1 hunks)
  • avocado/utils/process.py (43 hunks)
  • selftests/check.py (1 hunks)
  • selftests/functional/utils/process.py (3 hunks)
  • selftests/unit/utils/process.py (3 hunks)
  • spell.ignore (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .pylintrc_utils
  • selftests/functional/utils/process.py
🧰 Additional context used
🧬 Code graph analysis (1)
selftests/unit/utils/process.py (1)
avocado/utils/process.py (10)
  • binary_from_shell_cmd (407-435)
  • can_sudo (65-101)
  • has_capability (136-161)
  • pid_exists (164-187)
  • CmdResult (444-530)
  • kill_process_by_pattern (355-376)
  • system (1149-1212)
  • system_output (1216-1284)
  • getoutput (1288-1346)
  • getstatusoutput (1350-1413)
🪛 Ruff (0.14.8)
selftests/unit/utils/process.py

641-641: Function call with shell=True parameter identified, security issue

(S604)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (44)
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: Linux with Python 3.13
  • GitHub Check: Linux with Python 3.11
  • GitHub Check: Linux with Python 3.10
  • GitHub Check: Linux with Python 3.9
  • GitHub Check: Linux with Python 3.12
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: Podman spawner with 3rd party runner plugin
  • GitHub Check: Fedora selftests
  • GitHub Check: Build Package (wheel/tarball) for Python 3.13
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: Windows with Python 3.11
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Build Package (wheel/tarball) for Python 3.10
  • GitHub Check: Build Package (wheel/tarball) for Python 3.11
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Code Coverage (3.11)
  • GitHub Check: Static checks
  • GitHub Check: macOS with Python 3.11
🔇 Additional comments (12)
spell.ignore (1)

817-818: Allowlisting pkill / pkill's is consistent with existing spell-ignore conventions.
Possessive forms are already present elsewhere in this file, so this should behave as intended.

selftests/check.py (1)

30-32: LGTM! Test count updates reflect expanded coverage.

The updated test counts accurately reflect the new unit and functional tests added for process utilities in this PR.

avocado/utils/process.py (5)

143-147: Past review comment addressed successfully.

The docstring now clearly notes that capability names are UPPERCASE in capabilities(7) but must be lowercase in Python. This addresses the previous review feedback.


422-429: Past review comment addressed successfully.

The Linux-specific note has been added as requested in the previous review.


136-161: Well-executed API enhancement.

The addition of the optional pid parameter to has_capability is properly documented and backward compatible. The docstring clearly explains the new functionality with helpful examples.


407-435: Good improvement: ValueError now raised for invalid input.

The change to raise ValueError when no binary can be extracted is properly documented and provides better error handling than the previous implicit behavior. The docstring clearly describes when this exception is raised.


505-516: Enhanced error handling documentation.

The expanded docstring for stdout_text property clearly documents the TypeError exception case, improving the API's usability.

selftests/unit/utils/process.py (5)

1-1: Past review comment addressed: import moved to top.

The import errno statement has been moved to the top of the file as requested in previous review feedback, following PEP 8 guidelines.


360-366: Good test coverage for ValueError behavior.

This test properly validates the new ValueError exception raised by binary_from_shell_cmd when given invalid input, ensuring the API change is working as documented.


546-598: Comprehensive test coverage for can_sudo and has_capability.

These tests thoroughly exercise the various code paths including:

  • Root user behavior
  • Missing sudo binary
  • OSError handling
  • PID-specific capability checking

The use of mocking appropriately isolates the tests from system state.


599-631: Thorough testing of PID and process pattern operations.

The tests properly cover:

  • PID existence checks with different errno values (ESRCH, EPERM)
  • kill_process_by_pattern success and failure cases

This provides good coverage for the edge cases in process management.


632-682: Excellent coverage of system output functions.

These tests validate all the system/system_output/getoutput/getstatusoutput functions with various options including:

  • Exit code handling
  • Output capture
  • Newline stripping behavior

Note: The static analysis warning about shell=True on line 641 is a false positive—using shell=True is appropriate here for testing the false command behavior.

Process Module Preperation
Improvement of Unit/Functional Tests

Reference: avocado-framework/aautils#52
Assisted-By: Cursor-4-Sonnet
Signed-off-by: Harvey Lynden <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
selftests/unit/utils/process.py (1)

1-1: errno import moved to the top (PEP8 E402) — resolves prior feedback.

🧹 Nitpick comments (3)
selftests/unit/utils/process.py (2)

546-579: can_sudo() tests look solid; consider asserting key calls to reduce false positives.
Right now the “happy path” cases mostly assert only the boolean; adding assert_called_* on avocado.utils.path.find_command / avocado.utils.process.system would make failures easier to diagnose.


599-617: pid_exists() ESRCH/EPERM semantics are correctly exercised.
Minor: you may want to assert os.kill(pid, 0) was called in the ESRCH/EPERM tests too (like the “exists” test does) for consistency.

selftests/functional/utils/process.py (1)

238-253: Multiple SubProcess instances test is good basic coverage for parallel usage.
If you want to be stricter about “concurrently”, you could assert total wall time is closer to ~1s than ~3s, but current coverage is still useful.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ad3626 and 0663962.

📒 Files selected for processing (3)
  • selftests/check.py (1 hunks)
  • selftests/functional/utils/process.py (3 hunks)
  • selftests/unit/utils/process.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • selftests/check.py
🧰 Additional context used
🧬 Code graph analysis (1)
selftests/unit/utils/process.py (1)
avocado/utils/process.py (10)
  • binary_from_shell_cmd (407-435)
  • can_sudo (65-101)
  • has_capability (136-161)
  • pid_exists (164-187)
  • CmdResult (444-530)
  • kill_process_by_pattern (355-376)
  • system (1149-1212)
  • system_output (1216-1284)
  • getoutput (1288-1346)
  • getstatusoutput (1350-1413)
🪛 Ruff (0.14.8)
selftests/unit/utils/process.py

641-641: Function call with shell=True parameter identified, security issue

(S604)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (47)
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: Linux with Python 3.11
  • GitHub Check: Linux with Python 3.10
  • GitHub Check: Linux with Python 3.13
  • GitHub Check: Linux with Python 3.9
  • GitHub Check: Linux with Python 3.12
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: Version task debian:11.0
  • GitHub Check: Egg task fedora:40
  • GitHub Check: Build Package (wheel/tarball) for Python 3.11
  • GitHub Check: Windows with Python 3.13
  • GitHub Check: Build Package (wheel/tarball) for Python 3.13
  • GitHub Check: Build Package (wheel/tarball) for Python 3.12
  • GitHub Check: Build Package (wheel/tarball) for Python 3.10
  • GitHub Check: Build Package (wheel/tarball) for Python 3.9
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Static checks
  • GitHub Check: macOS with Python 3.11
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: Fedora selftests
  • GitHub Check: Code Coverage (3.11)
🔇 Additional comments (10)
selftests/unit/utils/process.py (3)

360-366: Good negative coverage for binary_from_shell_cmd() (empty + only var-assignments).


580-598: has_capability(..., pid=...) behavior is covered and asserts get_capabilities(pid) usage.


618-631: kill_process_by_pattern() tests cover success + failure without raising.

selftests/functional/utils/process.py (7)

125-142: Script templates are clear, and {{i}} escaping in LARGE_OUTPUT_SCRIPT is correct for .format().


158-169: _create_script() is a nice consolidation point (permissions + interpreter substitution).


215-229: Env propagation is validated for both process.run() and process.system_output().
Potential portability note: direct execution via shebang may not work on non-POSIX; if that’s in-scope, consider a platform skip.


230-237: getstatusoutput() integration test is straightforward and asserts exact output.


254-271: Large output capture test is appropriately strict (exact line count + boundary checks).


272-279: Encoding handling is verified via stdout_text and explicit encoding="utf-8".


280-286: Real-world-ish binary_from_shell_cmd() patterns are covered (env-prefix + sudo prefix).

Comment on lines +632 to +682
@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_system_function(self):
"""Test system() function returns exit code"""
exit_code = process.system(f"{ECHO_CMD} test", ignore_status=True)
self.assertEqual(exit_code, 0)

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_system_with_failure(self):
"""Test system() with failing command"""
exit_code = process.system("false", ignore_status=True, shell=True)
self.assertNotEqual(exit_code, 0)

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_system_output_function(self):
"""Test system_output() function returns output"""
output = process.system_output(f"{ECHO_CMD} -n hello", ignore_status=True)
self.assertEqual(output, b"hello")

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_system_output_strip_newline(self):
"""Test system_output() strips trailing newline"""
output = process.system_output(f"{ECHO_CMD} hello", ignore_status=True)
self.assertEqual(output, b"hello")

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_system_output_no_strip(self):
"""Test system_output() without stripping newline"""
output = process.system_output(
f"{ECHO_CMD} hello", ignore_status=True, strip_trail_nl=False
)
self.assertEqual(output, b"hello\n")

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_getoutput_function(self):
"""Test getoutput() function"""
output = process.getoutput(f"{ECHO_CMD} -n test")
self.assertEqual(output, "test")

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_getstatusoutput_function(self):
"""Test getstatusoutput() function"""
status, output = process.getstatusoutput(f"{ECHO_CMD} hello")
self.assertEqual(status, 0)
self.assertEqual(output, "hello")

@unittest.skipUnless(ECHO_CMD, "Echo command not available in system")
def test_getstatusoutput_with_failure(self):
"""Test getstatusoutput() with failing command"""
status, _output = process.getstatusoutput("false")
self.assertNotEqual(status, 0)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix Ruff S604 (shell=True) in test_system_with_failure to avoid lint/security gate failures.
If Ruff is enforced on tests, Line 641 will fail the check; false should work fine without shell=True.

-        exit_code = process.system("false", ignore_status=True, shell=True)
+        exit_code = process.system("false", ignore_status=True)
🧰 Tools
🪛 Ruff (0.14.8)

641-641: Function call with shell=True parameter identified, security issue

(S604)

🤖 Prompt for AI Agents
In selftests/unit/utils/process.py around lines 632 to 682, the
test_system_with_failure uses process.system("false", ignore_status=True,
shell=True) which triggers Ruff S604 (avoid shell=True); remove the shell=True
argument so the call is process.system("false", ignore_status=True) and adjust
the call signature if needed to run the command without a shell, keeping the
ignore_status=True and the assertNotEqual(status, 0) check unchanged.

Copy link
Contributor

@clebergnu clebergnu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All review points (including Gemini's) have been addressed, and it LGTM. Thanks!

@clebergnu clebergnu merged commit 897ad65 into avocado-framework:master Dec 12, 2025
67 of 70 checks passed
@github-project-automation github-project-automation bot moved this from Review Requested to Done 113 in Default project Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done 113

Development

Successfully merging this pull request may close these issues.

3 participants