Issues (43)

starstruct/tests/test_message.py (2 issues)

1
#!/usr/bin/env python3
2
3
"""Tests for the starstruct class"""
4
5
import enum
6
import unittest
7
import pytest
8
9
from starstruct.message import Message
10
from starstruct.modes import Mode
11
12
13
class SimpleEnum(enum.Enum):
14
    """Simple enum class for testing message pack/unpack"""
15
    one = 1
16
    two = 2
17
    three = 3
18
19
20
# pylint: disable=line-too-long,invalid-name
21
class TestStarStruct(unittest.TestCase):
22
    """StarStruct module tests"""
23
24
    teststruct = [
25
        ('a', 'b'),                            # signed byte: -128, 127
26
        ('pad1', '3x'),                        # 3 pad bytes
27
        ('b', 'H'),                            # unsigned short: 0, 65535
28
        ('pad2', 'x'),                         # 1 pad byte
29
        ('c', '10s'),                          # 10 byte string
30
        ('d', 'x'),                            # 1 pad byte
31
        ('e', '2H'),                           # 4 unsigned bytes: 0, 2^32-1
32
        ('type', 'B', SimpleEnum),             # unsigned byte, enum validated
33
        ('length', 'H', 'vardata'),            # unsigned short length field
34
        ('vardata',                            # variable length data
35
         Message('VarTest', [('x', 'B'), ('y', 'B')]),
36
         'length'),
37
        ('data', {                             # discriminated data
38
            SimpleEnum.one: Message('Struct1', [('y', 'B'), ('pad', '3x'), ('z', 'i')]),
39
            SimpleEnum.two: Message('Struct2', [('z', '20s')]),
40
            SimpleEnum.three: Message('Struct3', []),
41
        }, 'type'),
42
    ]
43
44
    testvalues = [
45
        {
46
            'a': -128,
47
            'b': 0,
48
            'c': '0123456789',
49
            'e': 0,
50
            'type': SimpleEnum.one,
51
            'length': 0,
52
            'vardata': [],
53
            'data': {
54
                'y': 50,
55
                'z': 0x5577AACC,
56
            },
57
        },
58
        {
59
            'a': 127,
60
            'b': 65535,
61
            'c': 'abcdefghij',
62
            'e': 0xFFFFFFFF,
63
            'type': SimpleEnum.two,
64
            'length': 2,
65
            'vardata': [
66
                {'x': 1, 'y': 2},
67
                {'x': 3, 'y': 4},
68
            ],
69
            'data': {
70
                'z': '0123456789abcdefghij',
71
            },
72
        },
73
        {
74
            'a': -1,
75
            'b': 32767,
76
            'c': '\n\tzyx',
77
            'e': 0x7FFFFFFF,
78
            'type': SimpleEnum.three,
79
            'length': 1,
80
            'vardata': [
81
                {'x': 255, 'y': 127},
82
            ],
83
            'data': {},
84
        },
85
        {
86
            'a': 100,
87
            'b': 100,
88
            'c': 'a0b1c2d3e4',
89
            'e': 10000,
90
            'type': SimpleEnum.one,
91
            'length': 10,
92
            'vardata': [
93
                {'x': 255, 'y': 127},
94
                {'x': 254, 'y': 128},
95
                {'x': 253, 'y': 129},
96
                {'x': 252, 'y': 130},
97
                {'x': 251, 'y': 131},
98
                {'x': 250, 'y': 132},
99
                {'x': 249, 'y': 133},
100
                {'x': 248, 'y': 134},
101
                {'x': 247, 'y': 135},
102
                {'x': 246, 'y': 136},
103
            ],
104
            'data': {
105
                'y': 100,
106
                'z': 2000,
107
            },
108
        },
109
    ]
110
111
    testbytes = {
112
        'little': [
113
            b'\x80\x00\x00\x00\x00\x00\x00\x30\x31' +
114
            b'\x32\x33\x34\x35\x36\x37\x38\x39\x00' +
115
            b'\x00\x00\x00\x00\x01\x00\x00\x32\x00\x00\x00\xCC\xAA\x77\x55',
116
            b'\x7F\x00\x00\x00\xFF\xFF\x00\x61\x62' +
117
            b'\x63\x64\x65\x66\x67\x68\x69\x6A\x00' +
118
            b'\xFF\xFF\xFF\xFF\x02\x02\x00\x01\x02' +
119
            b'\x03\x04\x30\x31\x32\x33\x34\x35\x36' +
120
            b'\x37\x38\x39\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a',
121
            b'\xFF\x00\x00\x00\xFF\x7F\x00\x0A\x09' +
122
            b'\x7A\x79\x78\x00\x00\x00\x00\x00\x00' +
123
            b'\xFF\xFF\xFF\x7F\x03\x01\x00\xFF\x7F',
124
            b'\x64\x00\x00\x00\x64\x00\x00\x61\x30' +
125
            b'\x62\x31\x63\x32\x64\x33\x65\x34\x00' +
126
            b'\x10\x27\x00\x00\x01\x0A\x00\xFF\x7F' +
127
            b'\xFE\x80\xFD\x81\xFC\x82\xFB\x83\xFA' +
128
            b'\x84\xF9\x85\xF8\x86\xF7\x87\xF6\x88' +
129
            b'\x64\x00\x00\x00\xD0\x07\x00\x00',
130
        ],
131
        'big': [
132
            b'\x80\x00\x00\x00\x00\x00\x00\x30\x31' +
133
            b'\x32\x33\x34\x35\x36\x37\x38\x39\x00' +
134
            b'\x00\x00\x00\x00\x01\x00\x00\x32\x00' +
135
            b'\x00\x00\x55\x77\xAA\xCC',
136
            b'\x7F\x00\x00\x00\xFF\xFF\x00\x61\x62' +
137
            b'\x63\x64\x65\x66\x67\x68\x69\x6A\x00' +
138
            b'\xFF\xFF\xFF\xFF\x02\x00\x02\x01\x02' +
139
            b'\x03\x04\x30\x31\x32\x33\x34\x35\x36' +
140
            b'\x37\x38\x39\x61\x62\x63\x64\x65\x66' +
141
            b'\x67\x68\x69\x6a',
142
            b'\xFF\x00\x00\x00\x7F\xFF\x00\x0A\x09' +
143
            b'\x7A\x79\x78\x00\x00\x00\x00\x00\x00' +
144
            b'\x7F\xFF\xFF\xFF\x03\x00\x01\xFF\x7F',
145
            b'\x64\x00\x00\x00\x00\x64\x00\x61\x30' +
146
            b'\x62\x31\x63\x32\x64\x33\x65\x34\x00' +
147
            b'\x00\x00\x27\x10\x01\x00\x0A\xFF\x7F' +
148
            b'\xFE\x80\xFD\x81\xFC\x82\xFB\x83\xFA' +
149
            b'\x84\xF9\x85\xF8\x86\xF7\x87\xF6\x88' +
150
            b'\x64\x00\x00\x00\x00\x00\x07\xD0',
151
        ],
152
    }
153
154
    def test_init_invalid_name(self):
155
        """Test invalid Message names."""
156
157
        for name in [None, '', 1, dict(), list()]:
158
            with self.subTest(name):  # pylint: disable=no-member
159
                with self.assertRaises(TypeError) as cm:
160
                    Message(name, self.teststruct)
161
                self.assertEqual(str(cm.exception), 'invalid name: {}'.format(name))
162
163
    def test_init_invalid_mode(self):
164
        """Test invalid Message modes."""
165
166
        for mode in ['=', 'stuff', 0, -1, 1]:
167
            with self.subTest(mode):  # pylint: disable=no-member
168
                with self.assertRaises(TypeError) as cm:
169
                    Message('test', self.teststruct, mode)
170
                self.assertEqual(str(cm.exception), 'invalid mode: {}'.format(mode))
171
172
    def test_init_empty_struct(self):
173
        """Test an empty Message."""
174
175
        val = Message('test', [])
176
        self.assertEqual(val._tuple._fields, ())  # pylint: disable=protected-access
177
178
    def test_pack_little_endian(self):
179
        """Test pack the test formats."""
180
        test_msg = Message('test', self.teststruct, Mode.Little)
181
        for idx in range(len(self.testvalues)):
182
            with self.subTest(idx):  # pylint: disable=no-member
183
                packed_msg = test_msg.pack(**self.testvalues[idx])
184
                self.assertEqual(self.testbytes['little'][idx], packed_msg)
185
186 View Code Duplication
    def test_unpack_little_endian(self):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
187
        """Test unpack the test formats."""
188
        test_msg = Message('test', self.teststruct, Mode.Little)
189
        assert test_msg.mode.to_byteorder() == 'little'
190
        for idx in range(len(self.testvalues)):
191
            with self.subTest(idx):  # pylint: disable=no-member
192
                (unpacked_partial_msg, unused) = test_msg.unpack_partial(self.testbytes['little'][idx] + b'\xde\xad')
193
                self.assertEqual(unused, b'\xde\xad')
194
                unpacked_msg = test_msg.unpack(self.testbytes['little'][idx])
195
                expected_tuple = test_msg.make(**self.testvalues[idx])  # pylint: disable=protected-access
196
                self.assertEqual(unpacked_msg, unpacked_partial_msg)
197
                self.assertEqual(unpacked_msg, expected_tuple)
198
199
    def test_pack_big_endian(self):
200
        """Test pack the test formats."""
201
        test_msg = Message('test', self.teststruct, Mode.Big)
202
        for idx in range(len(self.testvalues)):
203
            with self.subTest(idx):  # pylint: disable=no-member
204
                packed_msg = test_msg.pack(**self.testvalues[idx])
205
                self.assertEqual(self.testbytes['big'][idx], packed_msg)
206
207 View Code Duplication
    def test_unpack_big_endian(self):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
208
        """Test unpack the test formats."""
209
        test_msg = Message('test', self.teststruct, Mode.Big)
210
211
        assert test_msg.mode.to_byteorder() == 'big'
212
213
        for idx in range(len(self.testvalues)):
214
            with self.subTest(idx):  # pylint: disable=no-member
215
                (unpacked_partial_msg, unused) = test_msg.unpack_partial(self.testbytes['big'][idx] + b'\xde\xad')
216
                self.assertEqual(unused, b'\xde\xad')
217
                unpacked_msg = test_msg.unpack(self.testbytes['big'][idx])
218
                expected_tuple = test_msg.make(**self.testvalues[idx])  # pylint: disable=protected-access
219
                self.assertEqual(unpacked_msg, unpacked_partial_msg)
220
                self.assertEqual(unpacked_msg, expected_tuple)
221
222
    def test_bad_names(self):
223
        with pytest.raises(ValueError) as e:
224
            test_msg = Message('test', [
225
                ('pack', 'H'),
226
                ('_elements', 'H'),
227
                ('_fields', 'H'),
228
            ])
229
230
            print(test_msg)
231
232
        assert 'pack' in str(e)
233
        assert '_elements' in str(e)
234
        assert '_fields' in str(e)
235