From 169fcec63513d79d69378e08ee8018e091d53ca8 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 21 Aug 2026 14:02:53 -0500 Subject: [PATCH] fix(tests): power-cycle helper must run on Python 3.6, which CI uses The two power-cycle lifetime tests kept FAILING in CI after being taught to skip, and the skip was never the problem: _emulator_process() -> subprocess.run(..., capture_output=True, text=True) TypeError: __init__() got an unexpected keyword argument 'capture_output' `capture_output=` and `text=` are Python 3.7+. The CI python-keepkey container runs 3.6, so the helper raised inside subprocess before any of its own logic -- including the skip added last commit -- could run. Locally it passed because this machine runs 3.10. Replaced all three uses with stdout=/stderr=PIPE plus universal_newlines, which 3.6 and 3.10 both understand, and recorded why in the docstring so nobody "modernises" it back. Also: a missing lsof now returns None instead of raising. That is the same situation as a remote emulator -- the harness cannot identify the process, let alone restart it -- so it belongs on the skip path, not the failure path. A green tree should not go red because a container lacks a tool. Worth stating for the next person: this failure was invisible locally in every run, and the previous fix looked correct precisely because it was tested on the wrong interpreter. The environment is part of the test. Local run (Python 3.10, harness owns the emulator): 6/6, both power-cycle tests executing rather than skipping. --- tests/test_msg_session_trust_lifetime.py | 26 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/test_msg_session_trust_lifetime.py b/tests/test_msg_session_trust_lifetime.py index 0ffdd668..b4ad1a10 100644 --- a/tests/test_msg_session_trust_lifetime.py +++ b/tests/test_msg_session_trust_lifetime.py @@ -94,17 +94,25 @@ def probe_blob(): def _emulator_process(port): """(pid, exe, cwd) of the process BOUND to udp/port, or None. + NOTE: subprocess.run(capture_output=/text=) is Python 3.7+. The CI test + container runs 3.6, where passing them raises TypeError inside subprocess + and this helper dies before any of its own logic runs -- which is why the + power-cycle tests FAILED in CI instead of skipping. PIPE plus + universal_newlines is the spelling both understand. + Skips this test client's own connected socket, which lsof also reports on the same port but as a `local->remote` pair rather than a bare bind. """ try: out = subprocess.run(['lsof', '-nP', '-iUDP:%d' % port, '-Fpn'], - capture_output=True, text=True).stdout - except FileNotFoundError: - raise RuntimeError( - "lsof is required to find and restart the emulator for the " - "power-cycle tests; install it or run these against a device you " - "can power-cycle by hand") + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True).stdout + except (FileNotFoundError, OSError): + # No lsof: this harness cannot identify, let alone restart, the + # emulator process -- the same situation as a remote one. Report "not + # found" so _power_cycle() skips with its explanation, rather than + # failing a green tree over a missing tool. + return None pid = None for line in out.splitlines(): if line.startswith('p'): @@ -114,10 +122,12 @@ def _emulator_process(port): if '->' in name or not name.endswith(':%d' % port): continue exe = subprocess.run(['ps', '-o', 'comm=', '-p', str(pid)], - capture_output=True, text=True).stdout.strip() + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True).stdout.strip() cwd_out = subprocess.run( ['lsof', '-a', '-p', str(pid), '-d', 'cwd', '-Fn'], - capture_output=True, text=True).stdout + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True).stdout cwd = None for cwd_line in cwd_out.splitlines(): if cwd_line.startswith('n'):