import fcntl import os import pty import struct import termios from tamq.ptytap import ( PtyTap, TerminalOutputObserver, copy_winsize, observed_line, write_all, ) class RecordingBroker: def __init__(self): self.lines = [] def inspect_line(self, line): self.lines.append(line) return line.startswith("To:") def test_input_observer_accepts_raw_terminal_carriage_returns(): broker = RecordingBroker() observed = [] tap = PtyTap(["true"], broker, on_line=observed.append) buffer = bytearray() tap._observe_input(buffer, b"To:activity-core: hel") tap._observe_input(buffer, b"lo\rplain line\n") assert broker.lines == ["To:activity-core: hello", "plain line"] assert observed == ["To:activity-core: hello"] def test_input_observer_accepts_enhanced_terminal_enter_sequences(): broker = RecordingBroker() observed = [] tap = PtyTap(["true"], broker, on_line=observed.append) buffer = bytearray() tap._observe_input(buffer, b"To:activity-core: CSI-u\x1b[13;1") assert broker.lines == [] tap._observe_input(buffer, b":1uTo:other: modify\x1b[27;1;13~") tap._observe_input(buffer, b"plain\x1bOM") assert broker.lines == [ "To:activity-core: CSI-u", "To:other: modify", "plain", ] assert observed == ["To:activity-core: CSI-u", "To:other: modify"] def test_input_observer_ignores_terminal_replies_before_typed_line(): broker = RecordingBroker() observed = [] tap = PtyTap(["true"], broker, on_line=observed.append) buffer = bytearray() tap._observe_input(buffer, b"\x1b[9;1R\x1b[?1;2;4c") tap._observe_input(buffer, b"To:activity-core: Helol\x7f\x7flo\r") assert broker.lines == ["To:activity-core: Hello"] assert observed == ["To:activity-core: Hello"] def test_observed_line_removes_common_terminal_protocol_wrappers(): raw = ( b"\x1b[200~To:target: pasted\x1b[201~" b"\x1b]10;rgb:ffff/ffff/ffff\x07" b"\x1bP1$r0m\x1b\\" ) assert observed_line(raw) == "To:target: pasted" def test_output_observer_normalizes_ansi_and_deduplicates_redraws(): lines = [] observer = TerminalOutputObserver(lines.append) observer.feed(b"plain\r\n\x1b[31mTo:target: worker\x1b[0m\r") observer.feed(b"To:target: worker\r") assert lines == ["plain", "To:target: worker"] def test_output_observer_fails_closed_on_recent_operator_echo(): lines = [] observer = TerminalOutputObserver(lines.append) observer.note_operator_line("To:target: operator") observer.feed(b"To:target: operator\r\nTo:target: worker\r\n") assert lines == ["To:target: worker"] def test_output_observer_preserves_full_screen_program_gutters(): lines = [] observer = TerminalOutputObserver(lines.append) observer.feed( b"\x1b[4;1H\xe2\x80\xba To:target: operator echo" b"\x1b[5;1H\xe2\x80\xa2 tO:target: worker reply" b"\x1b[6;1H" ) assert lines == ["› To:target: operator echo", "• tO:target: worker reply"] def test_output_observer_emits_one_empty_line_for_crlf_terminator(): lines = [] observer = TerminalOutputObserver(lines.append) observer.feed(b"To:target: first\r\nfollowup\r\n\r\nordinary\r\n") assert lines == ["To:target: first", "followup", "", "ordinary"] def test_output_observer_infers_empty_full_screen_row_from_cursor_gap(): lines = [] observer = TerminalOutputObserver(lines.append) observer.feed( b"\x1b[4;1H\xe2\x80\xa2 To:target: first" b"\x1b[5;1H followup" b"\x1b[7;1Hprompt" ) assert lines == ["• To:target: first", " followup", ""] def test_copy_winsize_preserves_rows_columns_and_pixels(): source_master, source_slave = pty.openpty() target_master, target_slave = pty.openpty() expected = struct.pack("HHHH", 41, 103, 900, 1600) try: fcntl.ioctl(source_slave, termios.TIOCSWINSZ, expected) assert copy_winsize(source_slave, target_master) is True actual = fcntl.ioctl(target_slave, termios.TIOCGWINSZ, b"\0" * 8) assert actual == expected finally: for fd in (source_master, source_slave, target_master, target_slave): os.close(fd) def test_write_all_retries_partial_writes(monkeypatch): writes = [] def partial_write(fd, data): chunk = bytes(data[:2]) writes.append((fd, chunk)) return len(chunk) monkeypatch.setattr(os, "write", partial_write) write_all(9, b"abcde") assert writes == [(9, b"ab"), (9, b"cd"), (9, b"e")]