Completed
Push — master ( 5c1426...4ae943 )
by Aaron
24:12 queued 14:12
created

TestStarStruct.test_verifying_unpack()   F

Complexity

Conditions 16

Size

Total Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
c 1
b 0
f 0
dl 0
loc 82
rs 2.0442

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like TestStarStruct.test_verifying_unpack() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python3
2
3
"""Tests for the starstruct class"""
4
5
# import struct
6
import unittest
7
from binascii import crc32
8
9
import pytest
10
11
from starstruct.message import Message
12
# from starstruct.modes import Mode
13
14
15
# pylint: disable=line-too-long,invalid-name
16
class TestStarStruct(unittest.TestCase):
17
    """StarStruct module tests"""
18
19
    VarTest = Message('VarTest', [
20
        ('x', 'B'),
21
        ('y', 'B'),
22
    ])
23
24
    Repeated = Message('Repeated', [
25
        ('x', 'B'),
26
        ('z', 'H'),
27
    ])
28
29
    def test_single_element_2(self):
30
        TestStruct = Message('TestStruct', [
31
            ('length_in_objects', 'H', 'vardata'),
32
            ('vardata', self.VarTest, 'length_in_objects'),
33
        ])
34
35
        CRCedMessage = Message('CRCedMessage', [
36
            ('data', TestStruct),
37
            ('function_data', 'I', crc32, [b'data']),
38
        ])
39
40
        test_data = {
41
            'data': {
42
                'length_in_objects': 2,
43
                'vardata': [
44
                    {'x': 1, 'y': 2},
45
                    {'x': 3, 'y': 4},
46
                ],
47
            },
48
        }
49
50
        made = CRCedMessage.make(test_data)
51
        # assert len(made) == 5
52
        assert len(made.data.vardata) == 2
53
        assert made.data.vardata[0].x == 1
54
        assert made.data.vardata[0].y == 2
55
        assert made.function_data == crc32(TestStruct.pack(test_data['data']))
56
57
    def test_one_element(self):
58
        def crc32_wrapper(*args):
59
            return crc32(b''.join(args))
60
61
        CompareMessage = Message('CompareMessage', [
62
            ('length_in_objects', 'H', 'vardata'),
63
            ('vardata', self.VarTest, 'length_in_objects'),
64
        ])
65
66
        CRCedMessage = Message('TestStruct', [
67
            ('length_in_objects', 'H', 'vardata'),
68
            ('vardata', self.VarTest, 'length_in_objects'),
69
            ('function_data', 'I', crc32_wrapper, [b'length_in_objects', b'vardata']),
70
        ])
71
72
        test_data = {
73
            'length_in_objects': 2,
74
            'vardata': [
75
                {'x': 1, 'y': 2},
76
                {'x': 3, 'y': 4},
77
            ],
78
        }
79
80
        made = CRCedMessage.make(test_data)
81
        # assert len(made) == 5
82
        assert len(made.vardata) == 2
83
        assert made.vardata[0].x == 1
84
        assert made.vardata[0].y == 2
85
86
        assert made.function_data == crc32(CompareMessage.pack(test_data))
87
88
    def test_adding_element(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...
89
        def adder(x, y):
90
            return x + y
91
92
        AdderMessage = Message('AdderMessage', [
93
            ('item_a', 'H'),
94
            ('item_b', 'B'),
95
            ('function_data', 'I', adder, ['item_a', 'item_b']),
96
        ])
97
98
        test_data = {
99
            'item_a': 2,
100
            'item_b': 5,
101
        }
102
103
        made = AdderMessage.make(test_data)
104
        assert made.item_a == 2
105
        assert made.item_b == 5
106
107
        assert made.function_data == 7
108
109
    def test_adding_element_list(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...
110
        def adder(*args):
111
            return sum(args)
112
113
        AdderMessage = Message('AdderMessage', [
114
            ('item_a', 'H'),
115
            ('item_b', 'B'),
116
            ('item_c', 'B'),
117
            ('item_d', 'B'),
118
            ('item_e', 'B'),
119
            # Note, there is no item 'e' in the list of arguments
120
            ('function_data', 'I', adder, ['item_a', 'item_b', 'item_c', 'item_d']),
121
        ])
122
123
        # Test getting the correct result
124
        test_data = {
125
            'item_a': 2,
126
            'item_b': 5,
127
            'item_c': 7,
128
            'item_d': 4,
129
            'item_e': 6,
130
        }
131
132
        made = AdderMessage.make(test_data)
133
        assert made.item_a == 2
134
        assert made.item_b == 5
135
136
        assert made.function_data == 2 + 5 + 7 + 4
137
138
        # Check packing and unpacking
139
        packed = AdderMessage.pack(test_data)
140
        assert packed == b'\x02\x00\x05\x07\x04\x06\x12\x00\x00\x00'
141
        assert packed == made.pack()
142
143
        unpacked = AdderMessage.unpack(packed)
144
        assert made == unpacked
145
146
        # Test with correct result
147
        test_data_2 = {
148
            'item_a': 2,
149
            'item_b': 5,
150
            'item_c': 7,
151
            'item_d': 4,
152
            'item_e': 6,
153
            'function_data': 2 + 5 + 7 + 4,
154
        }
155
        made = AdderMessage.make(test_data_2)
156
        assert made.item_a == 2
157
        assert made.item_b == 5
158
159
        assert made.function_data == 2 + 5 + 7 + 4
160
161
        # Test with incorrect result
162
        test_data_2 = {
163
            'item_a': 2,
164
            'item_b': 5,
165
            'item_c': 7,
166
            'item_d': 4,
167
            'item_e': 6,
168
            'function_data': -1,
169
        }
170
171
        with pytest.raises(ValueError):
172
            made = AdderMessage.make(test_data_2)
173
174
    def test_no_error_message(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...
175
        def adder(*args):
176
            return sum(args)
177
178
        AdderMessage = Message('AdderMessage', [
179
            ('item_a', 'H'),
180
            ('item_b', 'B'),
181
            ('item_c', 'B'),
182
            ('item_d', 'B'),
183
            ('item_e', 'B'),
184
            # Note, there is no item 'e' in the list of arguments
185
            ('function_data', 'I', adder, ['item_a', 'item_b', 'item_c', 'item_d'], False),
186
        ])
187
188
        # Test with incorrect result
189
        test_data_2 = {
190
            'item_a': 2,
191
            'item_b': 5,
192
            'item_c': 7,
193
            'item_d': 4,
194
            'item_e': 6,
195
            'function_data': -1,
196
        }
197
198
        made = AdderMessage.make(test_data_2)
199
        assert made.function_data == -1
200
201
    def test_verifying_unpack(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...
202
        def adder(*args):
203
            return sum(args)
204
205
        AdderMessage = Message('AdderMessage', [
206
            ('item_a', 'H'),
207
            ('item_b', 'B'),
208
            ('item_c', 'B'),
209
            ('item_d', 'B'),
210
            ('item_e', 'B'),
211
            # Note, there is no item 'e' in the list of arguments
212
            ('function_data', 'I', adder, ['item_a', 'item_b', 'item_c', 'item_d']),
213
        ])
214
215
        # Test getting the correct result
216
        test_data = {
217
            'item_a': 2,
218
            'item_b': 5,
219
            'item_c': 7,
220
            'item_d': 4,
221
            'item_e': 6,
222
        }
223
224
        made = AdderMessage.make(test_data)
225
        assert made.item_a == 2
226
        assert made.item_b == 5
227
228
        assert made.function_data == 2 + 5 + 7 + 4
229
230
        # Check packing and unpacking
231
        packed = AdderMessage.pack(test_data)
232
        assert packed == b'\x02\x00\x05\x07\x04\x06\x12\x00\x00\x00'
233
        assert packed == made.pack()
234
235
        unpacked = AdderMessage.unpack(packed)
236
        assert made == unpacked
237
238
        # Now we modify the data we are going to unpack, and we should get an error
239
        modified_packed = b'\x02\x00\x05\x07\x04\x06\x11\x11\x11\x11'
240
241
        with pytest.raises(ValueError):
242
            unpacked = AdderMessage.unpack(modified_packed)
243
244
        AdderMessageFalse = Message('AdderMessageFalse', [
245
            ('item_a', 'H'),
246
            ('item_b', 'B'),
247
            ('item_c', 'B'),
248
            ('item_d', 'B'),
249
            ('item_e', 'B'),
250
            # Note, there is no item 'e' in the list of arguments
251
            ('function_data', 'I', adder, ['item_a', 'item_b', 'item_c', 'item_d'], False),
252
        ])
253
254
        # Test getting the correct result
255
        test_data = {
256
            'item_a': 2,
257
            'item_b': 5,
258
            'item_c': 7,
259
            'item_d': 4,
260
            'item_e': 6,
261
        }
262
263
        made = AdderMessageFalse.make(test_data)
264
        assert made.item_a == 2
265
        assert made.item_b == 5
266
267
        assert made.function_data == 2 + 5 + 7 + 4
268
269
        # Check packing and unpacking
270
        packed = AdderMessageFalse.pack(test_data)
271
        assert packed == b'\x02\x00\x05\x07\x04\x06\x12\x00\x00\x00'
272
        assert packed == made.pack()
273
274
        unpacked = AdderMessageFalse.unpack(packed)
275
        assert made == unpacked
276
277
        # Now we modify the data we are going to unpack, and we should get an error
278
        modified_packed = b'\x02\x00\x05\x07\x04\x06\x11\x11\x11\x11'
279
280
        # This time it won't fail because we set False for this message
281
        unpacked = AdderMessageFalse.unpack(modified_packed)
282
        assert unpacked.item_a == 2
283