Apply .gitattributes normalization to convert all CRLF line endings inherited from Windows-origin source files to Unix LF. 175 files, zero content changes.
109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
"""Tests for the stateful telnet IAC sequence stripper."""
|
|
|
|
from skywalker_tui.screens.starwars import _TelnetStripper
|
|
|
|
|
|
class TestTelnetStripper:
|
|
"""Edge cases for IAC parsing across chunk boundaries."""
|
|
|
|
def test_plain_text_passthrough(self):
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"hello world") == b"hello world"
|
|
|
|
def test_strips_will_command(self):
|
|
# IAC WILL ECHO = FF FB 01
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xfb\x01hello") == b"hello"
|
|
|
|
def test_strips_wont_command(self):
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xfc\x03data") == b"data"
|
|
|
|
def test_strips_do_command(self):
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xfd\x01data") == b"data"
|
|
|
|
def test_strips_dont_command(self):
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xfe\x01data") == b"data"
|
|
|
|
def test_escaped_0xff(self):
|
|
"""Doubled 0xFF = literal 0xFF byte in content."""
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xff") == b"\xff"
|
|
|
|
def test_sub_negotiation(self):
|
|
# IAC SB 0x01 ... IAC SE = FF FA 01 xx xx FF F0
|
|
s = _TelnetStripper()
|
|
data = b"before\xff\xfa\x01\x00\x00\xff\xf0after"
|
|
assert s.feed(data) == b"beforeafter"
|
|
|
|
def test_split_iac_across_chunks(self):
|
|
"""IAC command split: FF in chunk 1, FB 01 in chunk 2."""
|
|
s = _TelnetStripper()
|
|
out1 = s.feed(b"hello\xff")
|
|
out2 = s.feed(b"\xfb\x01world")
|
|
assert out1 == b"hello"
|
|
assert out2 == b"world"
|
|
|
|
def test_split_will_at_boundary(self):
|
|
"""IAC WILL split: FF FB in chunk 1, option byte in chunk 2."""
|
|
s = _TelnetStripper()
|
|
out1 = s.feed(b"aaa\xff\xfb")
|
|
out2 = s.feed(b"\x03bbb")
|
|
assert out1 == b"aaa"
|
|
assert out2 == b"bbb"
|
|
|
|
def test_split_sub_negotiation(self):
|
|
"""Sub-negotiation without closing IAC SE — buffers until next chunk."""
|
|
s = _TelnetStripper()
|
|
out1 = s.feed(b"x\xff\xfa\x01\x00")
|
|
out2 = s.feed(b"\xff\xf0y")
|
|
assert out1 == b"x"
|
|
assert out2 == b"y"
|
|
|
|
def test_multiple_commands_in_one_chunk(self):
|
|
s = _TelnetStripper()
|
|
data = b"\xff\xfb\x01\xff\xfc\x03text\xff\xfd\x01"
|
|
assert s.feed(data) == b"text"
|
|
|
|
def test_empty_input(self):
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"") == b""
|
|
|
|
def test_just_iac_byte(self):
|
|
"""Single 0xFF byte — should buffer, waiting for next byte."""
|
|
s = _TelnetStripper()
|
|
out1 = s.feed(b"\xff")
|
|
assert out1 == b""
|
|
out2 = s.feed(b"\xfb\x01done")
|
|
assert out2 == b"done"
|
|
|
|
def test_unknown_iac_command(self):
|
|
"""Unknown command byte after IAC — stripped as 2-byte sequence."""
|
|
s = _TelnetStripper()
|
|
assert s.feed(b"\xff\xf1text") == b"text"
|
|
|
|
|
|
class TestFrameParsing:
|
|
"""Tests for the ESC[H frame boundary detection logic."""
|
|
|
|
def test_frame_split_on_cursor_home(self):
|
|
"""ESC[H splits data into frames."""
|
|
data = b"frame1\x1b[Hframe2\x1b[Hframe3"
|
|
frames = data.split(b"\x1b[H")
|
|
assert frames == [b"frame1", b"frame2", b"frame3"]
|
|
|
|
def test_esc_j_in_frame(self):
|
|
"""ESC[J (clear to end) can be stripped from frame data."""
|
|
data = b"\x1b[Jsome text here"
|
|
clean = data.replace(b"\x1b[J", b"")
|
|
assert clean == b"some text here"
|
|
|
|
def test_empty_frames_between_homes(self):
|
|
"""Consecutive ESC[H produces empty frames (filtered in code)."""
|
|
data = b"\x1b[H\x1b[Htext"
|
|
frames = data.split(b"\x1b[H")
|
|
non_empty = [f for f in frames if f.strip()]
|
|
assert non_empty == [b"text"]
|