TestElementBitField   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 232
rs 9.8
wmc 31

6 Methods

Rating   Name   Duplication   Size   Complexity  
C test_out_of_range_values_pack() 0 48 7
B test_invalid_enum() 0 29 6
B test_out_of_range_values_unpack() 0 27 5
B test_unpack() 0 42 5
B test_valid_pack() 0 51 5
B test_not_valid() 0 27 3
1
#!/usr/bin/env python3
2
3
"""Tests for the elementenum class"""
4
5
import unittest
6
7
import struct
8
import enum
9
from starstruct.bitfield import BitField
10
from starstruct.packedbitfield import PackedBitField
11
from starstruct.elementbitfield import ElementBitField
12
13
14
class SimpleEnum(enum.Enum):
15
    """Simple enum class for testing message pack/unpack"""
16
    one = 1
17
    two = 2
18
    four = 4
19
20
21
class SimpleEnumWithZero(enum.Enum):
22
    """Simple enum class for testing message pack/unpack"""
23
    zero = 0
24
    one = 1
25
    two = 2
26
27
28
# pylint: disable=blacklisted-name
29
class StrEnum(enum.Enum):
30
    """string based enum class for testing message pack/unpack"""
31
    foo = 'foo'
32
    bar = 'bar'
33
34
35
# pylint: disable=line-too-long,invalid-name,no-self-use
36
class TestElementBitField(unittest.TestCase):
37
    """ElementBitField module tests"""
38
39
    def test_invalid_enum(self):
40
        """Test field formats that are valid ElementBitField elements."""
41
        # pylint: disable=unused-variable
42
43
        with self.assertRaises(TypeError) as cm:
44
            test_bitfield = BitField(StrEnum)  # noqa: F841
45
        msg = 'Enum {} members must have integer values'.format(repr(StrEnum))
46
        self.assertEqual(str(cm.exception), msg)
47
48
        with self.assertRaises(TypeError) as cm:
49
            test_bitfield = BitField(SimpleEnumWithZero)  # noqa: F841
50
        msg = 'Cannot construct BitField from {} with a value for 0: {}'.format(repr(SimpleEnumWithZero), SimpleEnumWithZero.zero)
51
        self.assertEqual(str(cm.exception), msg)
52
53
        with self.assertRaises(TypeError) as cm:
54
            test_packedbitfield = PackedBitField(SimpleEnumWithZero, SimpleEnumWithZero)  # noqa: F841
55
        msg = 'Duplicate fields not allowed: {}'.format((SimpleEnumWithZero, SimpleEnumWithZero))
56
        self.assertEqual(str(cm.exception), msg)
57
58
        test_bitfield = BitField(SimpleEnum)
59
        with self.assertRaises(TypeError) as cm:
60
            test_packedbitfield = PackedBitField(SimpleEnum, test_bitfield)  # noqa: F841
61
        msg = 'Duplicate fields not allowed: {}'.format((SimpleEnum, test_bitfield))
62
        self.assertEqual(str(cm.exception), msg)
63
64
        with self.assertRaises(TypeError) as cm:
65
            test_packedbitfield = PackedBitField(StrEnum, test_bitfield)  # noqa: F841
66
        msg = 'Enum {} members must have integer values'.format(repr(StrEnum))
67
        self.assertEqual(str(cm.exception), msg)
68
69
    def test_not_valid(self):
70
        """Test field formats that are not valid ElementEnum elements."""
71
72
        test_bitfield = BitField(SimpleEnum)
73
        test_packedbitfield = PackedBitField(SimpleEnumWithZero, test_bitfield)
74
75
        test_fields = [
76
            ('a', 'B', SimpleEnum),      # enum field
77
            ('a', 'B', StrEnum),       # enum field
78
            ('a', '4x', test_bitfield),  # 4 pad bytes
79
            ('b', 'z', test_bitfield),   # invalid
80
            ('b', 'b', test_bitfield),   # invalid
81
            ('c', '1', test_bitfield),   # invalid
82
            ('e', '9s', test_bitfield),  # invalid (no strings allowed)
83
            ('d', '/', test_bitfield),   # invalid
84
            ('a', '4x', test_packedbitfield),  # 4 pad bytes
85
            ('b', 'z', test_packedbitfield),   # invalid
86
            ('c', '1', test_packedbitfield),   # invalid
87
            ('e', '9s', test_packedbitfield),  # invalid (no strings allowed)
88
            ('d', '/', test_packedbitfield),   # invalid
89
            ('f', 'H'),               # unsigned short (no class)
90
        ]
91
92
        for field in test_fields:
93
            with self.subTest(field):  # pylint: disable=no-member
94
                out = ElementBitField.valid(field)
95
                self.assertFalse(out)
96
97
    def test_valid_pack(self):
98
        """Test packing valid enum values."""
99
100
        test_bitfield = BitField(SimpleEnum)
101
102
        field = ('a', 'B', test_bitfield)  # unsigned byte: 0, 256
103
        out = ElementBitField.valid(field)
104
        self.assertTrue(out)
105
106
        elem = ElementBitField(field)
107
        test_values = [
108
            ({'a': 2}, b'\x02'),
109
            ({'a': []}, b'\x00'),
110
            ({'a': None}, b'\x00'),
111
            ({'a': [SimpleEnum.one]}, b'\x01'),
112
            ({'a': ['one']}, b'\x01'),
113
            ({'a': [SimpleEnum.two]}, b'\x02'),
114
            ({'a': [SimpleEnum.one, SimpleEnum.two]}, b'\x03'),
115
            ({'a': [1, SimpleEnum.two]}, b'\x03'),
116
            ({'a': [1, SimpleEnum.two, 'four']}, b'\x07'),
117
        ]
118
        for (in_val, out_val) in test_values:
119
            with self.subTest((out_val, in_val)):  # pylint: disable=no-member
120
                ret = elem.pack(in_val)
121
                self.assertEqual(ret, out_val)
122
123
        test_packedbitfield = PackedBitField(SimpleEnumWithZero, test_bitfield)
124
        field = ('a', 'B', test_packedbitfield)  # unsigned byte: 0, 256
125
        out = ElementBitField.valid(field)
126
        self.assertTrue(out)
127
128
        self.assertEqual(list(test_packedbitfield._fields.keys()), [SimpleEnumWithZero, test_bitfield])
129
        self.assertEqual(test_packedbitfield._fields[test_bitfield], {'offset': 0, 'mask': 0x07, 'width': 3})
130
        self.assertEqual(test_packedbitfield._fields[SimpleEnumWithZero], {'offset': 3, 'mask': 0x18, 'width': 2})
131
132
        elem = ElementBitField(field)
133
        test_values = [
134
            ({'a': []}, b'\x00'),
135
            ({'a': None}, b'\x00'),
136
            ({'a': 0}, b'\x00'),  # 0 is a valid SimpleEnumWithZero
137
            ({'a': 4}, b'\x04'),
138
            ({'a': [SimpleEnum.one]}, b'\x01'),
139
            ({'a': [SimpleEnum.two]}, b'\x02'),
140
            ({'a': [SimpleEnum.one, SimpleEnum.two]}, b'\x03'),
141
            ({'a': ['zero', SimpleEnumWithZero.one, SimpleEnum.two]}, b'\x0a'),
142
            ({'a': [SimpleEnumWithZero.two, 'four']}, b'\x14'),
143
        ]
144
        for (in_val, out_val) in test_values:
145
            with self.subTest((out_val, in_val)):  # pylint: disable=no-member
146
                ret = elem.pack(in_val)
147
                self.assertEqual(ret, out_val)
148
149
    def test_out_of_range_values_pack(self):
150
        """Test packing invalid enum values."""
151
152
        test_bitfield = BitField(SimpleEnum)
153
154
        field = ('a', 'B', test_bitfield)  # unsigned byte: 0, 256
155
        out = ElementBitField.valid(field)
156
        self.assertTrue(out)
157
158
        elem = ElementBitField(field)
159
        test_values = [
160
            ({'a': -1}, -1),
161
            ({'a': 3}, 3),
162
            ({'a': 0}, 0),
163
            ({'a': [0, SimpleEnum.one]}, 0),
164
            ({'a': [SimpleEnum.one, -1]}, -1),
165
            ({'a': [SimpleEnum.two, 3]}, 3),
166
            ({'a': ['TWO']}, 'TWO'),
167
        ]
168
169
        msg = '{} is not a valid {}'
170
        for (in_val, bad_val) in test_values:
171
            with self.subTest((in_val, bad_val)):  # pylint: disable=no-member
172
                with self.assertRaises(ValueError) as cm:
173
                    elem.pack(in_val)
174
                self.assertEqual(str(cm.exception), msg.format(bad_val, 'SimpleEnum'))
175
176
        test_packedbitfield = PackedBitField(SimpleEnumWithZero, test_bitfield)
177
        field = ('a', 'B', test_packedbitfield)  # unsigned byte: 0, 256
178
        out = ElementBitField.valid(field)
179
        self.assertTrue(out)
180
181
        elem = ElementBitField(field)
182
        test_values = [
183
            ({'a': -1}, 'valid', -1),
184
            ({'a': 3}, 'valid', 3),
185
            ({'a': ['one']}, 'unique', 'one'),
186
            ({'a': [1, SimpleEnum.two]}, 'unique', 1),
187
            ({'a': [1, SimpleEnum.two, 'four']}, 'unique', 1),
188
            ({'a': [SimpleEnumWithZero.one, -1]}, 'valid', -1),
189
            ({'a': ['TWO']}, 'valid', 'TWO'),
190
        ]
191
        msg = '{} is not a {} {}'
192
        for (in_val, err_str, bad_val) in test_values:
193
            with self.subTest((in_val, err_str, bad_val)):  # pylint: disable=no-member
194
                with self.assertRaises(ValueError) as cm:
195
                    elem.pack(in_val)
196
                self.assertEqual(str(cm.exception), msg.format(bad_val, err_str, [SimpleEnumWithZero, test_bitfield]))
197
198
    def test_unpack(self):
199
        """Test unpacking valid enum values."""
200
201
        test_bitfield = BitField(SimpleEnum)
202
203
        field = ('a', 'B', test_bitfield)  # unsigned byte: 0, 256
204
        out = ElementBitField.valid(field)
205
        self.assertTrue(out)
206
        elem = ElementBitField(field)
207
        test_values = [
208
            (b'\x00', frozenset([])),
209
            (b'\xF8', frozenset([])),
210
            (b'\x01', frozenset([SimpleEnum.one])),
211
            (b'\x02', frozenset([SimpleEnum.two])),
212
            (b'\x03', frozenset([SimpleEnum.one, SimpleEnum.two])),
213
            (b'\xFF', frozenset([SimpleEnum.one, SimpleEnum.two, SimpleEnum.four])),
214
            (b'\xAA', frozenset([SimpleEnum.two])),
215
        ]
216
        for (in_val, out_val) in test_values:
217
            with self.subTest((in_val, out_val)):  # pylint: disable=no-member
218
                (ret, unused) = elem.unpack({}, in_val)
219
                self.assertEqual(unused, b'')
220
                self.assertEqual(ret, out_val)
221
222
        test_packedbitfield = PackedBitField(SimpleEnumWithZero, test_bitfield)
223
        field = ('a', 'B', test_packedbitfield)  # unsigned byte: 0, 256
224
        out = ElementBitField.valid(field)
225
        self.assertTrue(out)
226
227
        elem = ElementBitField(field)
228
        test_values = [
229
            (b'\x00', frozenset([SimpleEnumWithZero.zero])),
230
            (b'\x01', frozenset([SimpleEnumWithZero.zero, SimpleEnum.one])),
231
            (b'\x02', frozenset([SimpleEnumWithZero.zero, SimpleEnum.two])),
232
            (b'\x13', frozenset([SimpleEnumWithZero.two, SimpleEnum.one, SimpleEnum.two])),
233
            (b'\xAA', frozenset([SimpleEnumWithZero.one, SimpleEnum.two])),
234
        ]
235
        for (in_val, out_val) in test_values:
236
            with self.subTest((in_val, out_val)):  # pylint: disable=no-member
237
                (ret, unused) = elem.unpack({}, in_val)
238
                self.assertEqual(unused, b'')
239
                self.assertEqual(ret, out_val)
240
241
    def test_out_of_range_values_unpack(self):
242
        """Test packing invalid enum values."""
243
244
        test_bitfield = BitField(SimpleEnum)
245
246
        test_packedbitfield = PackedBitField(SimpleEnumWithZero, test_bitfield)
247
        field = ('a', 'B', test_packedbitfield)  # unsigned byte: 0, 256
248
        out = ElementBitField.valid(field)
249
        self.assertTrue(out)
250
251
        elem = ElementBitField(field)
252
        test_values = [
253
            (b'\xF8', 3),
254
        ]
255
        msg = '{} is not a valid {}'
256
        for (in_val, bad_val) in test_values:
257
            with self.subTest((in_val, bad_val)):  # pylint: disable=no-member
258
                int_in_val = struct.unpack('B', in_val)[0]
259
                with self.assertRaises(ValueError) as cm:
260
                    test_packedbitfield.unpack(int_in_val)
261
                self.assertEqual(str(cm.exception), msg.format(bad_val, 'SimpleEnumWithZero'))
262
263
                with self.assertRaises(ValueError) as cm:
264
                    elem.unpack({}, in_val)
265
                unpack_msg = 'Value: {0} was not valid for {1}\n\twith msg: {2},\n\tbuf: {3}'.format(
266
                    int_in_val, test_packedbitfield, {}, in_val)
267
                self.assertEqual(str(cm.exception), unpack_msg)
268