626 lines
18 KiB
Python
626 lines
18 KiB
Python
import math
|
|
from typing import Optional
|
|
|
|
import pytest
|
|
from hypothesis import given
|
|
from hypothesis import strategies as st
|
|
from typing_extensions import assert_type
|
|
|
|
from circuitmatter import tlv
|
|
|
|
# Test TLV encoding using examples from spec
|
|
|
|
# Type and Value
|
|
# Encoding (hex)
|
|
# Boolean false
|
|
# 08
|
|
# Boolean true
|
|
# 09
|
|
|
|
|
|
class Bool(tlv.Structure):
|
|
b = tlv.BoolMember(0)
|
|
|
|
|
|
class TestBool:
|
|
def test_bool_false_decode(self):
|
|
s = Bool.decode(b"\x15\x28\x00\x18")
|
|
assert str(s) == "{\n b = false\n}"
|
|
assert s.b is False
|
|
|
|
def test_bool_true_decode(self):
|
|
s = Bool.decode(b"\x15\x29\x00\x18")
|
|
assert str(s) == "{\n b = true\n}"
|
|
assert s.b is True
|
|
|
|
def test_bool_false_encode(self):
|
|
s = Bool()
|
|
s.b = False
|
|
assert s.encode().tobytes() == b"\x15\x28\x00\x18"
|
|
|
|
def test_bool_true_encode(self):
|
|
s = Bool()
|
|
s.b = True
|
|
assert s.encode().tobytes() == b"\x15\x29\x00\x18"
|
|
|
|
|
|
class SignedIntOneOctet(tlv.Structure):
|
|
i = tlv.NumberMember(0, "b")
|
|
|
|
|
|
class SignedIntTwoOctet(tlv.Structure):
|
|
i = tlv.NumberMember(0, "h")
|
|
|
|
|
|
class SignedIntFourOctet(tlv.Structure):
|
|
i = tlv.NumberMember(0, "i")
|
|
|
|
|
|
class SignedIntEightOctet(tlv.Structure):
|
|
i = tlv.NumberMember(0, "q")
|
|
|
|
|
|
# Signed Integer, 1-octet, value 42
|
|
# 00 2a
|
|
# Signed Integer, 1-octet, value -17
|
|
# 00 ef
|
|
# Signed Integer, 2-octet, value 42
|
|
# 01 2a 00
|
|
# Signed Integer, 4-octet, value -170000
|
|
# 02 f0 67 fd ff
|
|
# Signed Integer, 8-octet, value 40000000000
|
|
# 03 00 90 2f 50 09 00 00 00
|
|
class TestSignedInt:
|
|
def test_signed_int_42_decode(self):
|
|
s = SignedIntOneOctet.decode(b"\x15\x20\x00\x2a")
|
|
assert str(s) == "{\n i = 42\n}"
|
|
assert s.i == 42
|
|
|
|
def test_signed_int_negative_17_decode(self):
|
|
s = SignedIntOneOctet.decode(b"\x15\x20\x00\xef")
|
|
assert str(s) == "{\n i = -17\n}"
|
|
assert s.i == -17
|
|
|
|
def test_signed_int_42_two_octet_decode(self):
|
|
s = SignedIntTwoOctet.decode(b"\x15\x21\x00\x2a\x00")
|
|
assert str(s) == "{\n i = 42\n}"
|
|
assert s.i == 42
|
|
|
|
def test_signed_int_negative_170000_decode(self):
|
|
s = SignedIntFourOctet.decode(b"\x15\x22\x00\xf0\x67\xfd\xff")
|
|
assert str(s) == "{\n i = -170000\n}"
|
|
assert s.i == -170000
|
|
|
|
def test_signed_int_40000000000_decode(self):
|
|
s = SignedIntEightOctet.decode(b"\x15\x23\x00\x00\x90\x2f\x50\x09\x00\x00\x00")
|
|
assert str(s) == "{\n i = 40000000000\n}"
|
|
assert s.i == 40000000000
|
|
|
|
def test_signed_int_42_encode(self):
|
|
s = SignedIntOneOctet()
|
|
s.i = 42
|
|
assert s.encode().tobytes() == b"\x15\x20\x00\x2a\x18"
|
|
|
|
def test_signed_int_negative_17_encode(self):
|
|
s = SignedIntOneOctet()
|
|
s.i = -17
|
|
assert s.encode().tobytes() == b"\x15\x20\x00\xef\x18"
|
|
|
|
def test_signed_int_42_two_octet_encode(self):
|
|
s = SignedIntTwoOctet()
|
|
s.i = 42
|
|
assert s.encode().tobytes() == b"\x15\x20\x00\x2a\x18"
|
|
|
|
def test_signed_int_negative_170000_encode(self):
|
|
s = SignedIntFourOctet()
|
|
s.i = -170000
|
|
assert s.encode().tobytes() == b"\x15\x22\x00\xf0\x67\xfd\xff\x18"
|
|
|
|
def test_signed_int_40000000000_encode(self):
|
|
s = SignedIntEightOctet()
|
|
s.i = 40000000000
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x23\x00\x00\x90\x2f\x50\x09\x00\x00\x00\x18"
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
"octets,lower,upper",
|
|
[
|
|
(1, -128, 127),
|
|
(2, -32_768, 32_767),
|
|
(4, -2_147_483_648, 2_147_483_647),
|
|
(8, -9_223_372_036_854_775_808, 9_223_372_036_854_775_807),
|
|
],
|
|
)
|
|
def test_bounds_checks(self, octets, lower, upper):
|
|
class SignedIntStruct(tlv.Structure):
|
|
i = tlv.IntMember(None, signed=True, octets=octets)
|
|
|
|
s = SignedIntStruct()
|
|
|
|
with pytest.raises(ValueError):
|
|
s.i = lower - 1
|
|
|
|
with pytest.raises(ValueError):
|
|
s.i = upper + 1
|
|
|
|
s.i = lower
|
|
s.i = upper
|
|
|
|
|
|
class UnsignedIntOneOctet(tlv.Structure):
|
|
i = tlv.NumberMember(0, "B")
|
|
|
|
|
|
# Unsigned Integer, 1-octet, value 42U
|
|
# 04 2a
|
|
class TestUnsignedInt:
|
|
def test_unsigned_int_42_decode(self):
|
|
s = UnsignedIntOneOctet.decode(b"\x15\x24\x00\x2a\x18")
|
|
assert str(s) == "{\n i = 42U\n}"
|
|
assert s.i == 42
|
|
|
|
def test_unsigned_int_42_encode(self):
|
|
s = UnsignedIntOneOctet()
|
|
s.i = 42
|
|
assert s.encode().tobytes() == b"\x15\x24\x00\x2a\x18"
|
|
|
|
@pytest.mark.parametrize(
|
|
"octets,lower,upper",
|
|
[
|
|
(1, 0, 255),
|
|
(2, 0, 65_535),
|
|
(4, 0, 4_294_967_295),
|
|
(8, 0, 18_446_744_073_709_551_615),
|
|
],
|
|
)
|
|
def test_bounds_checks(self, octets, lower, upper):
|
|
class UnsignedIntStruct(tlv.Structure):
|
|
i = tlv.IntMember(None, signed=False, octets=octets)
|
|
|
|
s = UnsignedIntStruct()
|
|
|
|
with pytest.raises(ValueError):
|
|
s.i = lower - 1
|
|
|
|
with pytest.raises(ValueError):
|
|
s.i = upper + 1
|
|
|
|
s.i = lower
|
|
s.i = upper
|
|
|
|
@given(v=st.integers(min_value=0, max_value=255))
|
|
def test_roundtrip(self, v: int):
|
|
s = UnsignedIntOneOctet()
|
|
s.i = v
|
|
buffer = s.encode().tobytes()
|
|
|
|
s2 = UnsignedIntOneOctet.decode(buffer)
|
|
|
|
assert s2.i == s.i
|
|
assert str(s2) == str(s)
|
|
|
|
def test_nullability(self):
|
|
class Struct(tlv.Structure):
|
|
i = tlv.IntMember(None)
|
|
ni = tlv.IntMember(None, nullable=True)
|
|
|
|
s = Struct()
|
|
assert_type(s.i, int)
|
|
assert_type(s.ni, Optional[int])
|
|
|
|
s.ni = None
|
|
assert s.ni is None
|
|
|
|
with pytest.raises(ValueError):
|
|
s.i = None
|
|
|
|
|
|
# UTF-8 String, 1-octet length, "Hello!"
|
|
# 0c 06 48 65 6c 6c 6f 21
|
|
# UTF-8 String, 1-octet length, "Tschüs"
|
|
# 0c 07 54 73 63 68 c3 bc 73
|
|
class UTF8StringOneOctet(tlv.Structure):
|
|
s = tlv.UTF8StringMember(0, 16)
|
|
|
|
|
|
class TestUTF8String:
|
|
def test_utf8_string_hello_decode(self):
|
|
s = UTF8StringOneOctet.decode(b"\x15\x2c\x00\x06Hello!")
|
|
assert str(s) == '{\n s = "Hello!"\n}'
|
|
assert s.s == "Hello!"
|
|
|
|
def test_utf8_string_tschs_decode(self):
|
|
s = UTF8StringOneOctet.decode(b"\x15\x2c\x00\x07Tsch\xc3\xbcs")
|
|
assert str(s) == '{\n s = "Tschüs"\n}'
|
|
assert s.s == "Tschüs"
|
|
|
|
def test_utf8_string_hello_encode(self):
|
|
s = UTF8StringOneOctet()
|
|
s.s = "Hello!"
|
|
assert s.encode().tobytes() == b"\x15\x2c\x00\x06Hello!\x18"
|
|
|
|
def test_utf8_string_tschs_encode(self):
|
|
s = UTF8StringOneOctet()
|
|
s.s = "Tschüs"
|
|
assert s.encode().tobytes() == b"\x15\x2c\x00\x07Tsch\xc3\xbcs\x18"
|
|
|
|
@given(v=st.text(max_size=4))
|
|
def test_roundtrip(self, v: str):
|
|
s = UTF8StringOneOctet()
|
|
print(len(v))
|
|
s.s = v
|
|
buffer = s.encode().tobytes()
|
|
|
|
s2 = UTF8StringOneOctet.decode(buffer)
|
|
|
|
assert s2.s == s.s
|
|
assert str(s2) == str(s)
|
|
|
|
|
|
# Octet String, 1-octet length, octets 00 01 02 03 04
|
|
# encoded: 10 05 00 01 02 03 04
|
|
class OctetStringOneOctet(tlv.Structure):
|
|
s = tlv.OctetStringMember(0, 16)
|
|
|
|
|
|
class TestOctetString:
|
|
def test_octet_string_decode(self):
|
|
s = OctetStringOneOctet.decode(b"\x15\x30\x00\x05\x00\x01\x02\x03\x04\x18")
|
|
assert str(s) == "{\n s = 00 01 02 03 04\n}"
|
|
assert s.s == b"\x00\x01\x02\x03\x04"
|
|
|
|
def test_octet_string_encode(self):
|
|
s = OctetStringOneOctet()
|
|
s.s = b"\x00\x01\x02\x03\x04"
|
|
assert s.encode().tobytes() == b"\x15\x30\x00\x05\x00\x01\x02\x03\x04\x18"
|
|
|
|
@given(v=st.binary(max_size=16))
|
|
def test_roundtrip(self, v: bytes):
|
|
s = OctetStringOneOctet()
|
|
s.s = v
|
|
buffer = s.encode().tobytes()
|
|
|
|
s2 = OctetStringOneOctet.decode(buffer)
|
|
|
|
assert s2.s == s.s
|
|
assert str(s2) == str(s)
|
|
|
|
|
|
# Null
|
|
# 14
|
|
|
|
|
|
class Null(tlv.Structure):
|
|
n = tlv.BoolMember(0, nullable=True)
|
|
|
|
|
|
class NotNull(tlv.Structure):
|
|
n = tlv.BoolMember(0, nullable=True)
|
|
b = tlv.BoolMember(1)
|
|
|
|
|
|
class TestNull:
|
|
def test_null_decode(self):
|
|
s = Null.decode(b"\x15\x34\x00\x18")
|
|
assert str(s) == "{\n n = null\n}"
|
|
assert s.n is None
|
|
|
|
def test_null_encode(self):
|
|
s = Null()
|
|
s.n = None
|
|
assert s.encode().tobytes() == b"\x15\x34\x00\x18"
|
|
|
|
def test_nullable(self):
|
|
s = NotNull()
|
|
|
|
assert_type(s.b, bool)
|
|
with pytest.raises(ValueError):
|
|
s.b = None # type: ignore # testing runtime behaviour
|
|
|
|
|
|
# Single precision floating point 0.0
|
|
# 0a 00 00 00 00
|
|
# Single precision floating point (1.0 / 3.0)
|
|
# 0a ab aa aa 3e
|
|
# Single precision floating point 17.9
|
|
# 0a 33 33 8f 41
|
|
# Single precision floating point infinity (∞)
|
|
# 0a 00 00 80 7f
|
|
# Single precision floating point negative infinity
|
|
# 0a 00 00 80 ff
|
|
# (-∞)
|
|
# Double precision floating point 0.0
|
|
# 0b 00 00 00 00 00 00 00 00
|
|
# Double precision floating point (1.0 / 3.0)
|
|
# 0b 55 55 55 55 55 55 d5 3f
|
|
# Double precision floating point 17.9
|
|
# 0b 66 66 66 66 66 e6 31 40
|
|
# Double precision floating point infinity (∞)
|
|
# 0b 00 00 00 00 00 00 f0 7f
|
|
# Double precision floating point negative infinity 0b 00 00 00 00 00 00 f0 ff
|
|
# (-∞)
|
|
class FloatSingle(tlv.Structure):
|
|
f = tlv.FloatMember(0)
|
|
|
|
|
|
class FloatDouble(tlv.Structure):
|
|
f = tlv.FloatMember(0, octets=8)
|
|
|
|
|
|
class TestFloatSingle:
|
|
def test_precision_float_0_0_decode(self):
|
|
s = FloatSingle.decode(b"\x15\x2a\x00\x00\x00\x00\x00\x18")
|
|
assert str(s) == "{\n f = 0.0\n}"
|
|
assert s.f == 0.0
|
|
|
|
def test_precision_float_1_3_decode(self):
|
|
s = FloatSingle.decode(b"\x15\x2a\x00\xab\xaa\xaa\x3e\x18")
|
|
# assert str(s) == "{\n f = 0.3333333432674408\n}"
|
|
f = s.f
|
|
assert math.isclose(f, 1.0 / 3.0, rel_tol=1e-06)
|
|
|
|
def test_precision_float_17_9_decode(self):
|
|
s = FloatSingle.decode(b"\x15\x2a\x00\x33\x33\x8f\x41\x18")
|
|
assert str(s) == "{\n f = 17.899999618530273\n}"
|
|
assert math.isclose(s.f, 17.9, rel_tol=1e-06)
|
|
|
|
def test_precision_float_infinity_decode(self):
|
|
s = FloatSingle.decode(b"\x15\x2a\x00\x00\x00\x80\x7f\x18")
|
|
assert str(s) == "{\n f = inf\n}"
|
|
assert math.isinf(s.f)
|
|
|
|
def test_precision_float_negative_infinity_decode(self):
|
|
s = FloatSingle.decode(b"\x15\x2a\x00\x00\x00\x80\xff\x18")
|
|
assert str(s) == "{\n f = -inf\n}"
|
|
assert math.isinf(s.f)
|
|
|
|
def test_precision_float_0_0_encode(self):
|
|
s = FloatSingle()
|
|
s.f = 0.0
|
|
assert s.encode().tobytes() == b"\x15\x2a\x00\x00\x00\x00\x00\x18"
|
|
|
|
def test_precision_float_1_3_encode(self):
|
|
s = FloatSingle()
|
|
s.f = 1.0 / 3.0
|
|
assert s.encode().tobytes() == b"\x15\x2a\x00\xab\xaa\xaa\x3e\x18"
|
|
|
|
def test_precision_float_17_9_encode(self):
|
|
s = FloatSingle()
|
|
s.f = 17.9
|
|
assert s.encode().tobytes() == b"\x15\x2a\x00\x33\x33\x8f\x41\x18"
|
|
|
|
def test_precision_float_infinity_encode(self):
|
|
s = FloatSingle()
|
|
s.f = float("inf")
|
|
assert s.encode().tobytes() == b"\x15\x2a\x00\x00\x00\x80\x7f\x18"
|
|
|
|
def test_precision_float_negative_infinity_encode(self):
|
|
s = FloatSingle()
|
|
s.f = float("-inf")
|
|
assert s.encode().tobytes() == b"\x15\x2a\x00\x00\x00\x80\xff\x18"
|
|
|
|
@given(v=...)
|
|
def test_roundtrip_double(self, v: float):
|
|
s = FloatDouble()
|
|
s.f = v
|
|
buffer = s.encode().tobytes()
|
|
|
|
s2 = FloatDouble.decode(buffer)
|
|
|
|
assert (
|
|
(math.isnan(s.f) and math.isnan(s2.f))
|
|
or (s.f > 3.4028235e38 and s2.f == float("inf"))
|
|
or (s.f < -3.4028235e38 and s2.f == float("-inf"))
|
|
or math.isclose(s2.f, s.f, rel_tol=1e-7, abs_tol=1e-9)
|
|
)
|
|
|
|
@given(
|
|
v=st.floats(
|
|
# encoding to LE float32 raises OverflowError outside these ranges
|
|
# TODO: should we raise ValueError with a bounds check or encode -inf/inf?
|
|
min_value=(2**-126),
|
|
max_value=(2 - 2**-23) * 2**127,
|
|
),
|
|
)
|
|
def test_roundtrip_single(self, v: float):
|
|
s = FloatSingle()
|
|
s.f = v
|
|
buffer = s.encode().tobytes()
|
|
print("Buffer", buffer.hex(" "))
|
|
|
|
s2 = FloatSingle.decode(buffer)
|
|
|
|
assert (math.isnan(s.f) and math.isnan(s2.f)) or math.isclose(
|
|
s2.f, s.f, rel_tol=1e-7, abs_tol=1e-9
|
|
)
|
|
|
|
|
|
class TestFloatDouble:
|
|
def test_precision_float_0_0_decode(self):
|
|
s = FloatDouble.decode(b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
|
assert str(s) == "{\n f = 0.0\n}"
|
|
assert s.f == 0.0
|
|
|
|
def test_precision_float_1_3_decode(self):
|
|
s = FloatDouble.decode(b"\x15\x2b\x00\x55\x55\x55\x55\x55\x55\xd5\x3f")
|
|
# assert str(s) == "{\n f = 0.3333333333333333\n}"
|
|
f = s.f
|
|
assert math.isclose(f, 1.0 / 3.0, rel_tol=1e-06)
|
|
|
|
def test_precision_float_17_9_decode(self):
|
|
s = FloatDouble.decode(b"\x15\x2b\x00\x66\x66\x66\x66\x66\xe6\x31\x40")
|
|
assert str(s) == "{\n f = 17.9\n}"
|
|
assert math.isclose(s.f, 17.9, rel_tol=1e-06)
|
|
|
|
def test_precision_float_infinity_decode(self):
|
|
s = FloatDouble.decode(b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\xf0\x7f")
|
|
assert str(s) == "{\n f = inf\n}"
|
|
assert math.isinf(s.f)
|
|
|
|
def test_precision_float_negative_infinity_decode(self):
|
|
s = FloatDouble.decode(b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\xf0\xff")
|
|
assert str(s) == "{\n f = -inf\n}"
|
|
assert math.isinf(s.f)
|
|
|
|
def test_precision_float_0_0_encode(self):
|
|
s = FloatDouble()
|
|
s.f = 0.0
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18"
|
|
)
|
|
|
|
def test_precision_float_1_3_encode(self):
|
|
s = FloatDouble()
|
|
s.f = 1.0 / 3.0
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x2b\x00\x55\x55\x55\x55\x55\x55\xd5\x3f\x18"
|
|
)
|
|
|
|
def test_precision_float_17_9_encode(self):
|
|
s = FloatDouble()
|
|
s.f = 17.9
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x2b\x00\x66\x66\x66\x66\x66\xe6\x31\x40\x18"
|
|
)
|
|
|
|
def test_precision_float_infinity_encode(self):
|
|
s = FloatDouble()
|
|
s.f = float("inf")
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\xf0\x7f\x18"
|
|
)
|
|
|
|
def test_precision_float_negative_infinity_encode(self):
|
|
s = FloatDouble()
|
|
s.f = float("-inf")
|
|
assert (
|
|
s.encode().tobytes() == b"\x15\x2b\x00\x00\x00\x00\x00\x00\x00\xf0\xff\x18"
|
|
)
|
|
|
|
@given(v=...)
|
|
def test_roundtrip(self, v: float):
|
|
s = FloatDouble()
|
|
s.f = v
|
|
buffer = s.encode().tobytes()
|
|
|
|
s2 = FloatDouble.decode(buffer)
|
|
|
|
assert (
|
|
(math.isnan(s.f) and math.isnan(s2.f))
|
|
or (s.f > 1.8e308 and s2.f == float("inf"))
|
|
or (s.f < -1.8e308 and s2.f == float("-inf"))
|
|
or math.isclose(s2.f, s.f, rel_tol=2.22e-16, abs_tol=1e-15)
|
|
)
|
|
|
|
|
|
class InnerStruct(tlv.Structure):
|
|
a = tlv.IntMember(0, signed=True, optional=True, octets=4)
|
|
b = tlv.IntMember(1, signed=True, optional=True, octets=4)
|
|
|
|
|
|
class OuterStruct(tlv.Structure):
|
|
s = tlv.StructMember(0, InnerStruct)
|
|
|
|
|
|
class TestStruct:
|
|
def test_inner_struct_decode(self):
|
|
s = OuterStruct.decode(b"\x15\x35\x00\x20\x00\x2a\x20\x01\xef\x18\x18")
|
|
assert_type(s, OuterStruct)
|
|
assert_type(s.s, InnerStruct)
|
|
assert_type(s.s.a, Optional[int])
|
|
assert str(s) == "{\n s = {\n a = 42,\n b = -17\n }\n}"
|
|
assert s.s.a == 42
|
|
assert s.s.b == -17
|
|
|
|
def test_inner_struct_decode_empty(self):
|
|
s = OuterStruct.decode(b"\x15\x35\x00\x18\x18")
|
|
assert str(s) == "{\n s = {\n \n }\n}"
|
|
assert s.s.a is None
|
|
assert s.s.b is None
|
|
|
|
def test_inner_struct_encode(self):
|
|
s = OuterStruct()
|
|
inner = InnerStruct()
|
|
inner.a = 42
|
|
inner.b = -17
|
|
s.s = inner
|
|
assert s.encode().tobytes() == b"\x15\x35\x00\x20\x00\x2a\x20\x01\xef\x18\x18"
|
|
|
|
def test_inner_struct_encode_empty(self):
|
|
s = OuterStruct()
|
|
s.s = InnerStruct()
|
|
assert s.encode().tobytes() == b"\x15\x35\x00\x18\x18"
|
|
|
|
|
|
class FullyQualified(tlv.Structure):
|
|
a = tlv.IntMember((0xADA, 0xF00, 0x123), signed=True, optional=True, octets=4)
|
|
b = tlv.IntMember((0xADA, 0xF00, 0x12345), signed=True, optional=True, octets=4)
|
|
|
|
|
|
class TestFullyQualifiedTags:
|
|
def test_decode(self):
|
|
s = FullyQualified.decode(
|
|
b"\x15\xc2\xda\x0a\x00\x0f\x23\x01\x2a\x00\x00\x00\xe2\xda\x0a\x00\x0f\x45\x23\x01\x00\xef\xff\xff\xff\x18",
|
|
)
|
|
assert_type(s, FullyQualified)
|
|
assert_type(s.a, Optional[int])
|
|
assert str(s) == "{\n a = 42,\n b = -17\n}"
|
|
assert s.a == 42
|
|
assert s.b == -17
|
|
|
|
def test_encode(self):
|
|
s = FullyQualified()
|
|
s.a = 42
|
|
s.b = -17
|
|
assert (
|
|
s.encode().tobytes()
|
|
== b"\x15\xc0\xda\x0a\x00\x0f\x23\x01\x2a\xe0\xda\x0a\x00\x0f\x45\x23\x01\x00\xef\x18"
|
|
)
|
|
|
|
|
|
class InnerList(tlv.List):
|
|
a = tlv.IntMember(0, signed=True, optional=True, octets=4)
|
|
b = tlv.IntMember(1, signed=True, optional=True, octets=4)
|
|
|
|
|
|
class OuterStructList(tlv.Structure):
|
|
sublist = tlv.ListMember(0, InnerList)
|
|
|
|
|
|
class TestList:
|
|
def test_encode(self):
|
|
s = OuterStructList()
|
|
inner = InnerList()
|
|
inner.a = 42
|
|
inner.b = -17
|
|
s.sublist = inner
|
|
assert s.encode().tobytes() == b"\x15\x37\x00\x20\x00\x2a\x20\x01\xef\x18\x18"
|
|
|
|
|
|
class OuterStructArray(tlv.Structure):
|
|
a = tlv.ArrayMember(0, InnerList)
|
|
|
|
|
|
class TestArray:
|
|
def test_encode(self):
|
|
s = OuterStructArray()
|
|
inner = InnerList()
|
|
inner.a = 42
|
|
inner.b = -17
|
|
s.a = [inner]
|
|
assert (
|
|
s.encode().tobytes()
|
|
== b"\x15\x36\x00\x17\x20\x00\x2a\x20\x01\xef\x18\x18\x18"
|
|
)
|
|
|
|
def test_encode2(self):
|
|
s = OuterStructArray()
|
|
inner = InnerList()
|
|
inner.a = 42
|
|
inner.b = -17
|
|
s.a = [inner, inner]
|
|
assert (
|
|
s.encode().tobytes()
|
|
== b"\x15\x36\x00\x17\x20\x00\x2a\x20\x01\xef\x18\x17\x20\x00\x2a\x20\x01\xef\x18\x18\x18"
|
|
)
|