198 lines
10 KiB
Python
198 lines
10 KiB
Python
|
|
from collections import OrderedDict
|
|
from functools import partial
|
|
import struct
|
|
import types
|
|
import re
|
|
|
|
from six import binary_type, iterbytes
|
|
|
|
from Xlib.protocol import rq
|
|
from . import DummyDisplay, TestCase
|
|
|
|
|
|
dummy_display = DummyDisplay()
|
|
|
|
def pack(format, *args):
|
|
return struct.pack('=' + format, *args)
|
|
|
|
def packstr(s, padding=0):
|
|
return s.encode() + (b'\0' * padding)
|
|
|
|
|
|
class StructTest(object):
|
|
""" Test class helper for rq.Struct packing/unpacking support. """
|
|
|
|
struct = None
|
|
values_in = None
|
|
binary = None
|
|
values_out = None
|
|
|
|
def test_pack_value_dict(self):
|
|
self.assertBinaryEqual(self.struct.pack_value(dict(self.values_in)), self.binary)
|
|
|
|
def test_pack_value_tuple(self):
|
|
self.assertBinaryEqual(self.struct.pack_value(tuple(self.values_in.values())), self.binary)
|
|
|
|
def test_to_binary_args(self):
|
|
self.assertBinaryEqual(self.struct.to_binary(*self.values_in.values()), self.binary)
|
|
|
|
def test_to_binary_kwargs(self):
|
|
self.assertBinaryEqual(self.struct.to_binary(**dict(self.values_in)), self.binary)
|
|
|
|
def test_parse_binary(self):
|
|
values, remain = self.struct.parse_binary(self.binary, dummy_display)
|
|
self.assertEqual(values, rq.DictWrapper(dict(self.values_out)))
|
|
self.assertBinaryEmpty(remain)
|
|
|
|
|
|
def _struct_test(name, fields):
|
|
""" Generate a new Struct packing/unpacking test.
|
|
|
|
<fields> is a list of (field_name, field_type, field_value, field_binary):
|
|
- field_name:
|
|
name of the field, None if it's not a named field (e.g. LengthOf)
|
|
- field_type:
|
|
factory for the field type, taking one parameter only, the name of
|
|
the field and returning a rq.Field subclass instance
|
|
- field_value_in:
|
|
the raw value passed in when packing and expected output on unpacking
|
|
- field_binary:
|
|
the binary representation of the field value or a function to
|
|
convert it to binary for creating the expected result of packing
|
|
the struct
|
|
- field_value_out:
|
|
the raw value passed expected on output on unpacking
|
|
(optional, <field_value_in> is used if not specified)
|
|
"""
|
|
|
|
class_name = ''.join([part.capitalize() for part in re.sub(r'[^\w]+', ' ', name).split()])
|
|
class_name += 'StructTest'
|
|
struct_layout = []
|
|
values_in = OrderedDict()
|
|
binary = b''
|
|
values_out = OrderedDict()
|
|
for field_params in fields:
|
|
assert 4 <= len(field_params) <= 5
|
|
field_name, field_type, field_value_in, field_binary = field_params[0:4]
|
|
if len(field_params) == 5:
|
|
field_value_out = field_params[4]
|
|
else:
|
|
field_value_out = field_value_in
|
|
struct_layout.append(field_type(field_name))
|
|
if field_name is not None:
|
|
if field_value_in is not None:
|
|
values_in[field_name] = field_value_in
|
|
if field_value_out is not None:
|
|
values_out[field_name] = field_value_out
|
|
if isinstance(field_binary, binary_type):
|
|
binary += field_binary
|
|
elif isinstance(field_binary, (types.FunctionType, types.LambdaType, partial)):
|
|
binary += field_binary(field_value_in)
|
|
else:
|
|
raise ValueError('unsupported type for binary: {data} [{type}]'.format(
|
|
data=str(field_binary), type=type(field_binary))
|
|
)
|
|
class_dict = {
|
|
'struct': rq.Struct(*struct_layout),
|
|
'values_in': values_in,
|
|
'binary': binary,
|
|
'values_out': values_out,
|
|
}
|
|
globals()[class_name] = type(class_name, (StructTest, TestCase), class_dict)
|
|
|
|
|
|
_struct_test('card', (
|
|
('c8' , rq.Card8 , 0x42 , partial(pack, 'B')),
|
|
('c16', rq.Card16, 0x666 , partial(pack, 'H')),
|
|
('c32', rq.Card32, 0xdeadbeef, partial(pack, 'L')),
|
|
))
|
|
|
|
_struct_test('int', (
|
|
('i8' , rq.Int8 , -42 , partial(pack, 'b')),
|
|
('i16', rq.Int16, -666 , partial(pack, 'h')),
|
|
('i32', rq.Int32, -2147483648, partial(pack, 'l')),
|
|
))
|
|
|
|
_struct_test('list', (
|
|
(None , lambda name: rq.LengthOf('lc8', 1) , None , pack('B', 2) ),
|
|
(None , lambda name: rq.LengthOf('lc16', 2) , None , pack('H', 3) ),
|
|
(None , lambda name: rq.LengthOf('lc32', 4) , None , pack('L', 5) ),
|
|
('lc8' , lambda name: rq.List(name, rq.Card8 , pad=0), [0x42, 0xc3] , lambda v: pack('2B' , *v)),
|
|
('lc16' , lambda name: rq.List(name, rq.Card16, pad=0), [666, 1, 60143] , lambda v: pack('3H' , *v)),
|
|
('lc32' , lambda name: rq.List(name, rq.Card32, pad=0), [0xf02facb, 666, 1, 0x1043, 0xdeadbeef], lambda v: pack('5L' , *v)),
|
|
))
|
|
|
|
_struct_test('list pad', (
|
|
(None , lambda name: rq.LengthOf('lc8', 1) , None , pack('B', 2) ),
|
|
(None , lambda name: rq.LengthOf('lc16', 2) , None , pack('H', 3) ),
|
|
(None , lambda name: rq.LengthOf('lc32', 4) , None , pack('L', 5) ),
|
|
('lc8' , lambda name: rq.List(name, rq.Card8 , pad=1), [0x42, 0xc3] , lambda v: pack('2B2x', *v)),
|
|
('lc16' , lambda name: rq.List(name, rq.Card16, pad=1), [666, 1, 60143] , lambda v: pack('3H2x', *v)),
|
|
('lc32' , lambda name: rq.List(name, rq.Card32, pad=1), [0xf02facb, 666, 1, 0x1043, 0xdeadbeef], lambda v: pack('5L' , *v)),
|
|
))
|
|
|
|
_struct_test('fixed list', (
|
|
('lc8' , lambda name: rq.FixedList(name, 2, rq.Card8 , pad=0), [0x42, 0xc3] , lambda v: pack('2B' , *v)),
|
|
('lc16' , lambda name: rq.FixedList(name, 3, rq.Card16, pad=0), [666, 1, 60143] , lambda v: pack('3H' , *v)),
|
|
('lc32' , lambda name: rq.FixedList(name, 5, rq.Card32, pad=0), [0xf02facb, 666, 1, 0x1043, 0xdeadbeef], lambda v: pack('5L' , *v)),
|
|
))
|
|
|
|
_struct_test('fixed list pad', (
|
|
('lc8p' , lambda name: rq.FixedList(name, 2, rq.Card8 , pad=1), [0x42, 0xc3] , lambda v: pack('2B2x', *v)),
|
|
('lc16p', lambda name: rq.FixedList(name, 3, rq.Card16, pad=1), [666, 1, 60143] , lambda v: pack('3H2x', *v)),
|
|
('lc32p', lambda name: rq.FixedList(name, 5, rq.Card32, pad=1), [0xf02facb, 666, 1, 0x1043, 0xdeadbeef], lambda v: pack('5L' , *v)),
|
|
))
|
|
|
|
_struct_test('string8', (
|
|
(None, lambda name: rq.LengthOf('s1', 1) , None , pack('B', 7) ),
|
|
(None, lambda name: rq.LengthOf('s2', 2) , None , pack('H', 13)),
|
|
(None, lambda name: rq.LengthOf('s3', 4) , None , pack('L', 34)),
|
|
('s1', lambda name: rq.String8(name, pad=0), "testing" , packstr ),
|
|
('s2', lambda name: rq.String8(name, pad=0), "one two three" , packstr ),
|
|
('s3', lambda name: rq.String8(name, pad=0), "supercalifragilisticexpialidocious", packstr ),
|
|
))
|
|
|
|
_struct_test('string8 pad', (
|
|
(None, lambda name: rq.LengthOf('s1', 1) , None , pack('B', 7) ),
|
|
(None, lambda name: rq.LengthOf('s2', 2) , None , pack('H', 13) ),
|
|
(None, lambda name: rq.LengthOf('s3', 4) , None , pack('L', 34) ),
|
|
('s1', lambda name: rq.String8(name, pad=1), "testing" , partial(packstr, padding=1)),
|
|
('s2', lambda name: rq.String8(name, pad=1), "one two three" , partial(packstr, padding=3)),
|
|
('s3', lambda name: rq.String8(name, pad=1), "supercalifragilisticexpialidocious", partial(packstr, padding=2)),
|
|
))
|
|
|
|
_struct_test('simple string16', (
|
|
(None, lambda name: rq.LengthOf('s1', 1) , None , pack('B', 3) ),
|
|
(None, lambda name: rq.LengthOf('s2', 2) , None , pack('H', 3) ),
|
|
('s1', lambda name: rq.String16(name, pad=0), (0, 1, 2), lambda s: struct.pack('>' + 'H' * len(s), *s)),
|
|
# An 8-bits string is also allowed on input.
|
|
('s2', lambda name: rq.String16(name, pad=0), b'\x03\x04\x05', lambda s: struct.pack('>' + 'H' * len(s), *iterbytes(s)), (3, 4, 5)),
|
|
))
|
|
|
|
_struct_test('binary', (
|
|
(None, lambda name: rq.LengthOf('s1', 1) , None , pack('B', 7) ),
|
|
(None, lambda name: rq.LengthOf('s2', 2) , None , pack('H', 13)),
|
|
(None, lambda name: rq.LengthOf('s3', 4) , None , pack('L', 34)),
|
|
('s1', lambda name: rq.Binary(name, pad=0), b"testing" , lambda v: v),
|
|
('s2', lambda name: rq.Binary(name, pad=0), b"one two three" , lambda v: v),
|
|
('s3', lambda name: rq.Binary(name, pad=0), b"supercalifragilisticexpialidocious", lambda v: v),
|
|
))
|
|
|
|
_struct_test('binary pad', (
|
|
(None, lambda name: rq.LengthOf('s1', 1) , None , pack('B', 7) ),
|
|
(None, lambda name: rq.LengthOf('s2', 2) , None , pack('H', 13)),
|
|
(None, lambda name: rq.LengthOf('s3', 4) , None , pack('L', 34)),
|
|
('s1', lambda name: rq.Binary(name, pad=1), b"testing" , lambda v: v + b'\0' * 1),
|
|
('s2', lambda name: rq.Binary(name, pad=1), b"one two three" , lambda v: v + b'\0' * 3),
|
|
('s3', lambda name: rq.Binary(name, pad=1), b"supercalifragilisticexpialidocious", lambda v: v + b'\0' * 2),
|
|
))
|
|
|
|
_struct_test('fixed binary', (
|
|
('s1', lambda name: rq.FixedBinary(name, 7 ), b"testing" , lambda v: v),
|
|
('s2', lambda name: rq.FixedBinary(name, 13), b"one two three" , lambda v: v),
|
|
('s3', lambda name: rq.FixedBinary(name, 34), b"supercalifragilisticexpialidocious", lambda v: v),
|
|
# Make sure fixed binary fields are handled as static fields.
|
|
('c8', rq.Card8, 0x42, partial(pack, 'B')),
|
|
))
|