The _info_raises side_effect previously raised RuntimeError on every
logger.info call, which caused the pre-Popen "lsp.transport.starting"
call (transport.py:109) to raise before subprocess.Popen was ever
reached. This meant the cleanup guard at lines 160-186 was never
exercised, making the scenario a permanent no-op TDD stub rather than
a genuine regression guard.
Fix: make _info_raises conditional on the first positional argument
being "lsp.transport.started" (the post-Popen message). The pre-Popen
"lsp.transport.starting" call now passes through normally, Popen
succeeds (mocked), and the RuntimeError is raised inside the guarded
try/except block, triggering terminate() + wait() cleanup.
Remove @tdd_expected_fail from the scenario since the cleanup code at
transport.py:160-186 is in place and the scenario now passes.
Closes#7044
Closes#7044
The StdioTransport.start() method had an unprotected logger.info() call
after successful Popen(). If that call raised, the subprocess would
leak as an orphaned process. Wrap all post-spawn initialization in a
try/except guard: on any exception after spawn, terminate and wait for
the process (with kill fallback), reset state to None, then re-raise
so callers still get proper error semantics.
The existing stop() method cleanup pattern (terminate → wait → kill) is
mirrored here for consistency across the transport lifecycle.
Tests added:
- TDD scenario with @tdd_issue_7044 verifying subprocess cleanup on
post-Popen exception and state reset to None
- Explicit is_alive() scenarios covering both alive and not-alive states
Refactoring:
- Extracted _make_mock_process and _build_lsp_frame helpers into a
shared _ltcov_helpers module to keep step files under the 500-line
CONTRIBUTING.md limit.
ISSUES CLOSED: #7044
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate.
ISSUES CLOSED: #1232