ChillerDragon
4fd648025f
This sets message fields to `None` if they were not set. And also does not pack fields that are set to `None` BREAKING CHANGE: earlier to this commit the control close reason field was set to an empty string if it was not set. Now it is set to None
268 lines
8.2 KiB
Python
268 lines
8.2 KiB
Python
from twnet_parser.packer import \
|
|
Unpacker, pack_int, pack_str, pack_uint8, pack_be_uint16
|
|
from twnet_parser.packer import \
|
|
NO_SANITIZE, SANITIZE, SANITIZE_CC, SKIP_START_WHITESPACES
|
|
|
|
def test_unpack_eol() -> None:
|
|
u = Unpacker(b'\x01\x02\x03\x01foo\x00bar\x00')
|
|
assert u.get_int() == 1
|
|
assert u.get_int() == 2
|
|
assert u.get_int() == 3
|
|
assert u.get_int() == 1
|
|
assert u.get_str() == 'foo'
|
|
assert u.get_str() == 'bar'
|
|
|
|
# now we reached the end of the data
|
|
|
|
assert u.get_optional_int() is None
|
|
assert u.get_optional_str() is None
|
|
|
|
def test_pack_uint8():
|
|
assert pack_uint8(1) == b'\x01'
|
|
assert pack_uint8(2) == b'\x02'
|
|
assert pack_uint8(3) == b'\x03'
|
|
|
|
def test_pack_be_uint16():
|
|
assert pack_be_uint16(1) == b'\x00\x01'
|
|
assert pack_be_uint16(2) == b'\x00\x02'
|
|
assert pack_be_uint16(3) == b'\x00\x03'
|
|
assert pack_be_uint16(256) == b'\x01\x00'
|
|
|
|
def test_unpack_uint8():
|
|
u = Unpacker(b'\x01')
|
|
assert u.get_uint8() == 1
|
|
u = Unpacker(b'\x02')
|
|
assert u.get_uint8() == 2
|
|
|
|
def test_unpack_multiple_uint8():
|
|
u = Unpacker(b'\x01\x02')
|
|
assert u.get_uint8() == 1
|
|
assert u.get_uint8() == 2
|
|
|
|
def test_unpack_be_uint16():
|
|
u = Unpacker(b'\x00\x01')
|
|
assert u.get_be_uint16() == 1
|
|
u = Unpacker(b'\x00\x02')
|
|
assert u.get_be_uint16() == 2
|
|
u = Unpacker(b'\x00\x33')
|
|
assert u.get_be_uint16() == 51
|
|
u = Unpacker(b'\x01\x00')
|
|
assert u.get_be_uint16() == 256
|
|
# this is needed for the connless lis2
|
|
# packed_addresses field
|
|
u = Unpacker(b'\x20\x6F')
|
|
assert u.get_be_uint16() == 8303
|
|
|
|
def test_unpack_multiple_be_uint16():
|
|
u = Unpacker(b'\x00\x01\x00\x02')
|
|
assert u.get_be_uint16() == 1
|
|
assert u.get_be_uint16() == 2
|
|
|
|
def test_unpack_ints_and_strings() -> None:
|
|
u = Unpacker(b'\x01\x02\x03\x01foo\x00bar\x00')
|
|
assert u.get_int() == 1
|
|
assert u.get_int() == 2
|
|
assert u.get_int() == 3
|
|
assert u.get_int() == 1
|
|
assert u.get_str() == 'foo'
|
|
assert u.get_str() == 'bar'
|
|
|
|
def test_pack_bools() -> None:
|
|
assert pack_int(False) == b'\x00'
|
|
assert pack_int(True) == b'\x01'
|
|
|
|
assert pack_int(0) == b'\x00'
|
|
assert pack_int(1) == b'\x01'
|
|
|
|
def test_unpack_bools() -> None:
|
|
data: bytes = b'\x00\x01'
|
|
u = Unpacker(data)
|
|
assert (u.get_int() == 1) is False
|
|
assert (u.get_int() == 1) is True
|
|
|
|
def test_simple_repack() -> None:
|
|
data: bytes = pack_str('hello world')
|
|
assert data == b'hello world\x00'
|
|
data += pack_int(22)
|
|
|
|
u = Unpacker(data)
|
|
assert u.get_str() == 'hello world'
|
|
assert u.get_int() == 22
|
|
|
|
def test_non_ascii_repack() -> None:
|
|
data: bytes = pack_str('💩')
|
|
assert data == '💩'.encode('utf-8') + b'\x00'
|
|
|
|
u = Unpacker(data)
|
|
assert u.get_str() == '💩'
|
|
|
|
def test_string_sanitize_should_not_affect_plain_ascii():
|
|
u = Unpacker(b'\x41\x41\x00\x42\x42\x00\x42\x42\x00\x42\x42\x00')
|
|
assert u.get_str(NO_SANITIZE) == 'AA'
|
|
assert u.get_str(SANITIZE) == 'BB'
|
|
assert u.get_str(SANITIZE_CC) == 'BB'
|
|
assert u.get_str(SKIP_START_WHITESPACES) == 'BB'
|
|
assert u.get_str() == ''
|
|
|
|
def test_string_no_sanitize_keep_space():
|
|
u = Unpacker(b'\x20\x41\x41\x00')
|
|
assert u.get_str(NO_SANITIZE) == ' AA'
|
|
|
|
def test_string_no_sanitize_should_not_crash_on_invalid_utf8():
|
|
u = Unpacker(b'\x80\n\x41\x41\n\x00')
|
|
assert u.get_str(NO_SANITIZE) == '\nAA\n'
|
|
|
|
def test_string_no_sanitize_keep_newline():
|
|
u = Unpacker(b'\n\x41\x41\n\x00')
|
|
assert u.get_str(NO_SANITIZE) == '\nAA\n'
|
|
|
|
def test_string_no_sanitize_keep_r_and_t():
|
|
u = Unpacker(b'\r\x41\x41\t\x00')
|
|
assert u.get_str(NO_SANITIZE) == '\rAA\t'
|
|
|
|
def test_string_sanitize_keep_r_and_t():
|
|
u = Unpacker(b'\r\x41\x41\t\x00')
|
|
assert u.get_str(SANITIZE) == '\rAA\t'
|
|
|
|
def test_string_sanitize_by_default_keep_r_and_t():
|
|
u = Unpacker(b'\r\x41\x41\t\x00')
|
|
assert u.get_str() == '\rAA\t'
|
|
|
|
def test_string_sanitize_strip_x01_x02():
|
|
u = Unpacker(b'\x41\x01\x02\x41\x00')
|
|
assert u.get_str(SANITIZE) == 'A A'
|
|
|
|
def test_string_sanitize_cc_strip_r_and_t():
|
|
u = Unpacker(b'\x41\x01\x02\x41\x00')
|
|
assert u.get_str(SANITIZE_CC) == 'A A'
|
|
u = Unpacker(b'\r\x41\x41\t\x00')
|
|
assert u.get_str(SANITIZE_CC) == ' AA '
|
|
u = Unpacker(b'\r\t\r\r\r\x00')
|
|
assert u.get_str(SANITIZE_CC) == ' '
|
|
|
|
def test_string_sanitize_cc_strip_x01_x02():
|
|
u = Unpacker(b'\x01\x02\x00')
|
|
assert u.get_str(SANITIZE_CC) == ' '
|
|
u = Unpacker(b'foo\x01bar\x02\x01baz\x00')
|
|
assert u.get_str(SANITIZE_CC) == 'foo bar baz'
|
|
|
|
# TODO: should we strip that? Do not think so since tw is full of unicode
|
|
# only single bytes lower than 32 are stripped
|
|
# but multi byte unicode with bytes over 32 should be fine
|
|
# def test_string_sanitize_cc_strip_poop_emoji():
|
|
# u = Unpacker('💩'.encode('utf-8') + b'\x00')
|
|
# assert u.get_str(SANITIZE_CC) == len('💩'.encode('utf-8')) * ' '
|
|
# u = Unpacker(b'foo' + '💩'.encode('utf-8') + b'bar\x00')
|
|
# assert u.get_str(SANITIZE_CC) == f"foo{len('💩'.encode('utf-8')) * ' '}bar"
|
|
|
|
def test_string_sanitize_cc_keep_poop_emoji():
|
|
u = Unpacker('💩'.encode('utf-8') + b'\x00')
|
|
assert u.get_str(SANITIZE_CC) == '💩'
|
|
u = Unpacker(b'foo' + '💩'.encode('utf-8') + b'bar\x00')
|
|
assert u.get_str(SANITIZE_CC) == 'foo💩bar'
|
|
|
|
def test_string_sanitize_keep_poop_emoji():
|
|
u = Unpacker('💩'.encode('utf-8') + b'\x00')
|
|
assert u.get_str(SANITIZE) == '💩'
|
|
u = Unpacker(b'foo' + '💩'.encode('utf-8') + b'bar\x00')
|
|
assert u.get_str(SANITIZE) == 'foo💩bar'
|
|
|
|
def test_string_no_sanitize_keep_poop_emoji():
|
|
u = Unpacker('💩'.encode('utf-8') + b'\x00')
|
|
assert u.get_str(NO_SANITIZE) == '💩'
|
|
u = Unpacker(b'foo' + '💩'.encode('utf-8') + b'bar\x00')
|
|
assert u.get_str(NO_SANITIZE) == 'foo💩bar'
|
|
|
|
def test_string_skip_start_whitespaces_strip_leading_spaces():
|
|
u = Unpacker(b' \x00')
|
|
assert u.get_str(SKIP_START_WHITESPACES) == ''
|
|
u = Unpacker(b'\t \t \t foo bar \t baz\x00')
|
|
assert u.get_str(SKIP_START_WHITESPACES) == 'foo bar \t baz'
|
|
|
|
# TODO: check tw code what this should do
|
|
# def test_string_skip_start_whitespaces_strip_x01_x02():
|
|
# """
|
|
# https://chillerdragon.github.io/teeworlds-protocol/07/fundamentals.html#string_packing
|
|
#
|
|
# UB in this spec :shrug:
|
|
# """
|
|
# u = Unpacker(b'\x01\x02\x00')
|
|
# assert u.get_str(SKIP_START_WHITESPACES) == ''
|
|
# u = Unpacker(b'\tfoo\x01bar\x02\x01baz\x00')
|
|
# assert u.get_str(SKIP_START_WHITESPACES) == 'foo bar baz'
|
|
|
|
def test_raw_repack_at_end() -> None:
|
|
data: bytes = b''
|
|
data += pack_int(1)
|
|
data += pack_str('a')
|
|
data += b'rawr'
|
|
|
|
u = Unpacker(data)
|
|
assert u.get_int() == 1
|
|
assert u.get_str() == 'a'
|
|
assert u.get_raw() == b'rawr'
|
|
|
|
def test_raw_sized_repack() -> None:
|
|
data: bytes = b''
|
|
data += pack_int(1)
|
|
data += pack_str('a')
|
|
data += b'rawr'
|
|
data += b'abc'
|
|
data += b'\x00\x00'
|
|
data += b'\x01\x02'
|
|
data += pack_int(1)
|
|
data += pack_int(2)
|
|
data += b'\x00\x00'
|
|
|
|
u = Unpacker(data)
|
|
assert u.get_int() == 1
|
|
assert u.get_str() == 'a'
|
|
assert u.get_raw(4) == b'rawr'
|
|
assert u.get_raw(3) == b'abc'
|
|
assert u.get_raw(2) == b'\x00\x00'
|
|
assert u.get_raw(2) == b'\x01\x02'
|
|
assert u.get_int() == 1
|
|
assert u.get_int() == 2
|
|
assert u.get_raw(2) == b'\x00\x00'
|
|
|
|
def test_multi_repack() -> None:
|
|
strs: list[str] = [
|
|
'foo',
|
|
'bar',
|
|
'baz',
|
|
'',
|
|
'yeeeeeeeeeeeeeeeeeee' \
|
|
'eeeeeeeeeeeeeeeeeeee' \
|
|
'eeeeeeeeeeeeeeeeeeee' \
|
|
'eeeeeeeeeeeeeeeeeeee' \
|
|
'eeeeeeeeeeeeeeeeeeee' \
|
|
'eeeeeeeeeeeeeeeeeeee' \
|
|
'eeeppiiiiiiiiiiiiiii',
|
|
'a b c d e f',
|
|
'nameless tee',
|
|
'(1)nameless tee',
|
|
'[D](1)nameless t']
|
|
|
|
ints: list[int] = [
|
|
0,
|
|
111111111,
|
|
222222222,
|
|
649010,
|
|
-1,
|
|
-19882,
|
|
29299]
|
|
|
|
# pack
|
|
data: bytes = b''
|
|
for string in strs:
|
|
data += pack_str(string)
|
|
for num in ints:
|
|
data += pack_int(num)
|
|
|
|
# unpack
|
|
u = Unpacker(data)
|
|
for string in strs:
|
|
assert u.get_str() == string
|
|
for num in ints:
|
|
assert u.get_int() == num
|