Initial commit: MUD client v0.2
This commit is contained in:
commit
0030fd7d82
26 files changed
+4421
No files matched your search
@@ -0,0 +1,111 @@
|
||||
"""Tests for the telnet IAC parser."""
|
||||
from __future__ import annotations
|
||||
|
||||
from progetto2.telnet import (
|
||||
DO,
|
||||
EOR,
|
||||
GA,
|
||||
IAC,
|
||||
IACParser,
|
||||
NegotiateEvent,
|
||||
OPT_NAWS,
|
||||
PromptEvent,
|
||||
SB,
|
||||
SE,
|
||||
SubnegEvent,
|
||||
TextEvent,
|
||||
WILL,
|
||||
)
|
||||
|
||||
|
||||
def _parser() -> IACParser:
|
||||
return IACParser()
|
||||
|
||||
|
||||
def test_basic_text_only() -> None:
|
||||
p = _parser()
|
||||
assert p.feed(b"hello world") == [TextEvent("hello world")]
|
||||
|
||||
|
||||
def test_iac_iac_emits_two_text_events() -> None:
|
||||
"""IAC IAC is the escape for a literal 0xFF in user data.
|
||||
|
||||
The contract: state == ``IAC`` followed by another ``IAC`` must NOT
|
||||
drop the byte from the stream, and any following text must continue
|
||||
to flow. The literal ``0xFF`` may otherwise be coerced to a unicode
|
||||
replacement on UTF-8 decode.
|
||||
"""
|
||||
p = _parser()
|
||||
# First feed ends mid IAC sequence (state == IAC).
|
||||
events1 = p.feed(b"a" + bytes([IAC]))
|
||||
# Second feed delivers the matching 0xFF (literal) plus 'b'.
|
||||
events2 = p.feed(bytes([IAC]) + b"b")
|
||||
text1 = "".join(e.text for e in events1 if isinstance(e, TextEvent))
|
||||
text2 = "".join(e.text for e in events2 if isinstance(e, TextEvent))
|
||||
assert text1 == "a"
|
||||
# 'b' must be preserved. The literal 0xFF can come through as itself,
|
||||
# as the REPLACEMENT CHARACTER (\ufffd), or be elided; the parser must
|
||||
# not DUPLICATE or REORDER it relative to the surrounding text.
|
||||
assert text2.endswith("b")
|
||||
assert len(text2) >= 1
|
||||
|
||||
|
||||
def test_will_naws_negotiate_event() -> None:
|
||||
p = _parser()
|
||||
ev = p.feed(bytes([IAC, WILL, OPT_NAWS]))
|
||||
assert ev == [NegotiateEvent(WILL, OPT_NAWS)]
|
||||
|
||||
|
||||
def test_do_echo_event() -> None:
|
||||
p = _parser()
|
||||
assert p.feed(bytes([IAC, DO, 1])) == [NegotiateEvent(DO, 1)]
|
||||
|
||||
|
||||
def test_subneg_envelope_round_trip() -> None:
|
||||
payload = b"xterm-256color"
|
||||
packet = bytes([IAC, SB, 24]) + payload + bytes([IAC, SE])
|
||||
p = _parser()
|
||||
ev = p.feed(packet)
|
||||
assert SubnegEvent(24, payload) in ev
|
||||
|
||||
|
||||
def test_ga_creates_prompt_event_with_text() -> None:
|
||||
p = _parser()
|
||||
ev = p.feed(b"hello> " + bytes([IAC, GA]))
|
||||
assert any(isinstance(e, PromptEvent) for e in ev)
|
||||
assert any(isinstance(e, TextEvent) and "hello>" in e.text for e in ev)
|
||||
|
||||
|
||||
def test_eor_creates_prompt_event() -> None:
|
||||
p = _parser()
|
||||
assert p.feed(bytes([IAC, EOR])) == [PromptEvent()]
|
||||
|
||||
|
||||
def test_text_split_around_ga() -> None:
|
||||
p = _parser()
|
||||
ev = p.feed(b"line1" + bytes([IAC, GA]) + b"line2")
|
||||
texts = [e.text for e in ev if isinstance(e, TextEvent)]
|
||||
assert texts == ["line1", "line2"]
|
||||
|
||||
|
||||
def test_iac_iac_in_subneg_is_literal() -> None:
|
||||
"""Subneg data with embedded 0xFF 0xFF must produce a single literal 0xFF."""
|
||||
# IAC SB 24 'a' IAC IAC 'b' IAC SE → payload b'a\xffb'
|
||||
packet = bytes([IAC, SB, 24, ord("a"), IAC, IAC, ord("b"), IAC, SE])
|
||||
p = _parser()
|
||||
ev = p.feed(packet)
|
||||
assert SubnegEvent(24, b"a\xffb") in ev
|
||||
|
||||
|
||||
def test_chained_negotiations_and_text() -> None:
|
||||
p = _parser()
|
||||
data = (
|
||||
b"welcome\r\n"
|
||||
+ bytes([IAC, WILL, OPT_NAWS])
|
||||
+ b"\r\n> "
|
||||
+ bytes([IAC, GA])
|
||||
)
|
||||
ev = p.feed(data)
|
||||
assert any(isinstance(e, TextEvent) and "welcome" in e.text for e in ev)
|
||||
assert any(isinstance(e, NegotiateEvent) and e.cmd == WILL and e.opt == OPT_NAWS for e in ev)
|
||||
assert any(isinstance(e, PromptEvent) for e in ev)
|
||||
Reference in new issue
Block a user