Completed
Push — master ( 9a0979...321b56 )
by
unknown
09:26
created

TestStarStruct   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 210
Duplicated Lines 10.48 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
c 7
b 0
f 0
dl 22
loc 210
rs 10
wmc 26

8 Methods

Rating   Name   Duplication   Size   Complexity  
A test_init_empty_struct() 1 5 1
A test_init_invalid_name() 6 8 4
D test_unpack_little_endian() 1 11 3
A test_pack_big_endian() 0 7 3
A test_pack_little_endian() 7 7 3
B test_bad_names() 0 13 5
A test_unpack_big_endian() 0 11 3
A test_init_invalid_mode() 4 8 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
220
        with pytest.raises(ValueError) as e:
221
            test_msg = Message('test', [
222
                ('pack', 'H'),
223
                ('_elements', 'H'),
224
                ('_fields', 'H'),
225
            ])
226
227
            print(test_msg)
228
229
        assert 'pack' in str(e)
230
        assert '_elements' in str(e)
231
        assert '_fields' in str(e)
232