Feature: LSP StdioTransport coverage As a developer maintaining the LSP transport layer I need thorough tests for StdioTransport So that all error paths and normal flows are exercised # ── start() error paths ───────────────────────────────────────── @tdd_issue @tdd_issue_7044 Scenario: ltcov start cleans up subprocess when post-Popen init fails Given ltcov I create a StdioTransport for command "cat" And ltcov Popen is mocked to succeed with a running process And ltcov logger.info after spawn raises RuntimeError When ltcov I try to start the transport Then ltcov an error occurred during start() And ltcov the internal process should be None And ltcov the mock process should have been terminated And ltcov the mock process should have been waited on Scenario: ltcov start raises RuntimeError when already alive Given ltcov I create a StdioTransport for command "cat" And ltcov the transport has a running mock process When ltcov I try to start the transport Then ltcov the error should be a RuntimeError with message "already started" @tdd_issue @tdd_issue_7044 Scenario: ltcov start is alive after successful spawn Given ltcov I create a StdioTransport for command "cat" When ltcov I try to start the transport Then ltcov the transport should be alive And ltcov the internal process should not be None Scenario: ltcov is_alive returns False when no process Given ltcov I create a StdioTransport for command "cat" When ltcov I check if the transport is alive Then ltcov the is_alive result should be false Scenario: ltcov start raises LspError on FileNotFoundError Given ltcov I create a StdioTransport for command "nonexistent_binary_xyz" And ltcov Popen is mocked to raise FileNotFoundError When ltcov I try to start the transport Then ltcov the error should be an LspError with message "not found" Scenario: ltcov start raises LspError on OSError Given ltcov I create a StdioTransport for command "bad_command" And ltcov Popen is mocked to raise OSError with "Permission denied" When ltcov I try to start the transport Then ltcov the error should be an LspError with message "Failed to start" # ── stop() paths ──────────────────────────────────────────────── Scenario: ltcov stop returns None when not started Given ltcov I create a StdioTransport for command "echo" When ltcov I stop the transport Then ltcov the stop result should be None Scenario: ltcov stop returns exit code when process already exited Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process that already exited with code 0 When ltcov I stop the transport Then ltcov the stop result should be 0 And ltcov the internal process should be None Scenario: ltcov stop returns exit code when process already exited with error Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process that already exited with code 1 When ltcov I stop the transport Then ltcov the stop result should be 1 Scenario: ltcov stop force kills on timeout Given ltcov I create a StdioTransport for command "sleep" And ltcov the transport has a mock process that ignores terminate When ltcov I stop the transport with timeout 0.1 Then ltcov the mock process should have been killed And ltcov the stop result should be an integer # ── send_message() paths ──────────────────────────────────────── Scenario: ltcov send_message raises RuntimeError when not started Given ltcov I create a StdioTransport for command "echo" When ltcov I try to send a message Then ltcov the error should be a RuntimeError with message "not started" Scenario: ltcov send_message raises RuntimeError when stdin is None Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with no stdin When ltcov I try to send a message Then ltcov the error should be a RuntimeError with message "not started" Scenario: ltcov send_message writes Content-Length framed JSON-RPC Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with writable stdin When ltcov I send the message {"jsonrpc":"2.0","method":"initialize","id":1} Then ltcov the written bytes should contain "Content-Length:" And ltcov the written bytes should contain "initialize" Scenario: ltcov send_message raises BrokenPipeError on dead process Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with broken stdin When ltcov I try to send a message Then ltcov the error should be a BrokenPipeError # ── read_message() paths ──────────────────────────────────────── Scenario: ltcov read_message raises RuntimeError when not started Given ltcov I create a StdioTransport for command "echo" When ltcov I try to read a message Then ltcov the error should be a RuntimeError with message "not started" Scenario: ltcov read_message raises RuntimeError when stdout is None Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with no stdout When ltcov I try to read a message Then ltcov the error should be a RuntimeError with message "not started" Scenario: ltcov read_message returns None on select timeout Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with empty stdout And ltcov select is mocked to return not ready When ltcov I read a message with timeout 1.0 Then ltcov the read result should be None Scenario: ltcov read_message returns None on EOF Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with stdout that returns EOF When ltcov I read a message with timeout 1.0 Then ltcov the read result should be None Scenario: ltcov read_message uses default timeout when None Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with stdout that returns EOF When ltcov I read a message with default timeout Then ltcov the read result should be None Scenario: ltcov read_message parses valid Content-Length message Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with a valid JSON-RPC response When ltcov I read a message with timeout 5.0 Then ltcov the read result should have key "jsonrpc" with value "2.0" Scenario: ltcov read_message returns None on invalid content-length value Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with invalid content-length header When ltcov I read a message with timeout 5.0 Then ltcov the read result should be None Scenario: ltcov read_message returns None when no content-length header Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with no content-length header When ltcov I read a message with timeout 5.0 Then ltcov the read result should be None Scenario: ltcov read_message raises ValueError on oversized message Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with oversized content-length When ltcov I try to read a message Then ltcov the error should be a ValueError with message "too large" Scenario: ltcov read_message returns None on body select timeout Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with headers but body times out When ltcov I read a message with timeout 5.0 Then ltcov the read result should be None Scenario: ltcov read_message returns None on truncated body Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with truncated body When ltcov I read a message with timeout 5.0 Then ltcov the read result should be None Scenario: ltcov read_message returns None on invalid JSON body Given ltcov I create a StdioTransport for command "echo" And ltcov the transport has a mock process with invalid JSON body When ltcov I read a message with timeout 5.0 Then ltcov the read result should be None # ── subprocess cleanup on failed initialization (issue #10597) ──────────── Scenario: ltcov _process is reset to None after FileNotFoundError in start Given ltcov I create a StdioTransport for command "nonexistent_binary_xyz" And ltcov Popen is mocked to raise FileNotFoundError When ltcov I try to start the transport Then ltcov the error should be an LspError with message "not found" And ltcov _process must be None after start failure Scenario: ltcov _process is reset to None after OSError in start Given ltcov I create a StdioTransport for command "bad_command" And ltcov Popen is mocked to raise OSError with "Permission denied" When ltcov I try to start the transport Then ltcov the error should be an LspError with message "Failed to start" And ltcov _process must be None after start failure