|
1
|
|
|
#!/usr/bin/env python3 |
|
2
|
|
|
|
|
3
|
|
|
"""Tests for the elementfixedpoint class""" |
|
4
|
|
|
|
|
5
|
|
|
import unittest |
|
6
|
|
|
|
|
7
|
|
|
from decimal import Decimal |
|
8
|
|
|
|
|
9
|
|
|
from starstruct.elementfixedpoint import ElementFixedPoint, get_fixed_bits |
|
10
|
|
|
from starstruct.message import Message |
|
11
|
|
|
from starstruct.modes import Mode |
|
12
|
|
|
|
|
13
|
|
|
|
|
14
|
|
|
# pylint: disable=no-self-use |
|
15
|
|
|
class TestElementFixedPointHelpers(unittest.TestCase): |
|
16
|
|
|
"""Test the helpers for this class""" |
|
17
|
|
|
def test_invalid_formats(self): |
|
18
|
|
|
with self.assertRaises(ValueError): |
|
19
|
|
|
get_fixed_bits(5, 'Z', 1) |
|
20
|
|
|
|
|
21
|
|
|
with self.assertRaises(ValueError): |
|
22
|
|
|
get_fixed_bits(8, 'f', 4) |
|
23
|
|
|
|
|
24
|
|
|
# TODO: Make sure that it won't accept more than one number? |
|
25
|
|
|
|
|
26
|
|
|
def test_valid_formats(self): |
|
27
|
|
|
get_fixed_bits(5, 'h', 1) |
|
28
|
|
|
get_fixed_bits(15, 'L', 0) |
|
29
|
|
|
|
|
30
|
|
|
def test_invalid_higher_bits(self): |
|
31
|
|
|
with self.assertRaises(ValueError): |
|
32
|
|
|
get_fixed_bits(257, 'i', 32) |
|
33
|
|
|
|
|
34
|
|
|
with self.assertRaises(ValueError): |
|
35
|
|
|
get_fixed_bits(256, 'i', 32) |
|
36
|
|
|
|
|
37
|
|
|
with self.assertRaises(ValueError): |
|
38
|
|
|
get_fixed_bits('hello', 'i', 3) |
|
39
|
|
|
|
|
40
|
|
|
with self.assertRaises(ValueError): |
|
41
|
|
|
get_fixed_bits(Decimal('20'), 'h', 17) |
|
42
|
|
|
|
|
43
|
|
|
with self.assertRaises(ValueError): |
|
44
|
|
|
get_fixed_bits(42, 'i', 33) |
|
45
|
|
|
|
|
46
|
|
|
def test_valid_higher_bits(self): |
|
47
|
|
|
"""Just make sure these all don't fail""" |
|
48
|
|
|
get_fixed_bits(255, 'I', 8) |
|
49
|
|
|
get_fixed_bits(15.5, 'I', 4) |
|
50
|
|
|
get_fixed_bits(22.75, 'i', 11) |
|
51
|
|
|
get_fixed_bits(Decimal('13.0'), 'q', 16) |
|
52
|
|
|
|
|
53
|
|
|
def test_zero(self): |
|
54
|
|
|
assert get_fixed_bits(0, 'H', 8) == (0).to_bytes(2, 'big') |
|
55
|
|
|
|
|
56
|
|
|
def test_basic_example(self): |
|
57
|
|
|
assert get_fixed_bits(Decimal('0.9375'), 'B', 4) == (15).to_bytes(1, 'little') |
|
58
|
|
|
|
|
59
|
|
|
def test_another_example(self): |
|
60
|
|
|
assert get_fixed_bits(Decimal('12.9375'), 'h', 4) == (192 + 15).to_bytes(2, 'little') |
|
61
|
|
|
assert get_fixed_bits(Decimal('12.9375'), 'i', 4) == (192 + 15).to_bytes(4, 'little') |
|
62
|
|
|
assert get_fixed_bits(Decimal('12.9375'), 'q', 4) == (192 + 15).to_bytes(8, 'little') |
|
63
|
|
|
|
|
64
|
|
|
|
|
65
|
|
|
class TestElementFixedPoint(unittest.TestCase): |
|
66
|
|
|
"""ElementFixedPoint module tests""" |
|
67
|
|
|
|
|
68
|
|
|
def test_valid(self): |
|
69
|
|
|
"""Test field formats that are valid fixedpoint elements.""" |
|
70
|
|
|
|
|
71
|
|
|
test_fields = [ |
|
72
|
|
|
('a', 'F', 'i', 8), |
|
73
|
|
|
('b', 'F', 'h', 4), |
|
74
|
|
|
('c', 'F', 'h', 7), |
|
75
|
|
|
] |
|
76
|
|
|
|
|
77
|
|
|
for field in test_fields: |
|
78
|
|
|
with self.subTest(field): # pylint: disable=no-member |
|
79
|
|
|
out = ElementFixedPoint.valid(field) |
|
80
|
|
|
self.assertTrue(out) |
|
81
|
|
|
|
|
82
|
|
|
def test_not_valid(self): |
|
83
|
|
|
"""Test field formats that are not valid fixedpoint elements.""" |
|
84
|
|
|
|
|
85
|
|
|
test_fields = [ |
|
86
|
|
|
('a', 'PF', 16, 8), # Must have numbers preceding the F |
|
87
|
|
|
('b', '3D', 8, 8), # D is not a valid prefix. Must be F for fixedpoint |
|
88
|
|
|
('c', 'D', 8.0, 8), # Must be int, not float |
|
89
|
|
|
('d', 'D', 8, 16), # bytes must be larger than precision. |
|
90
|
|
|
('e', 'D', 8), # Must be of length four |
|
91
|
|
|
] |
|
92
|
|
|
|
|
93
|
|
|
for field in test_fields: |
|
94
|
|
|
with self.subTest(field): # pylint: disable=no-member |
|
95
|
|
|
out = ElementFixedPoint.valid(field) |
|
96
|
|
|
self.assertFalse(out) |
|
97
|
|
|
|
|
98
|
|
|
def test_valid_pack(self): |
|
99
|
|
|
"""Test packing valid fixed point values.""" |
|
100
|
|
|
|
|
101
|
|
|
precision = 8 |
|
102
|
|
|
field = ('a', 'F', 'i', precision) |
|
103
|
|
|
self.assertTrue(ElementFixedPoint.valid(field)) |
|
104
|
|
|
elem = ElementFixedPoint(field, Mode.Big) |
|
105
|
|
|
|
|
106
|
|
|
test_values = [ |
|
107
|
|
|
({'a': '4'}, 4), |
|
108
|
|
|
({'a': 13.5}, 13.5), |
|
109
|
|
|
({'a': '13.5'}, '13.5'), |
|
110
|
|
|
({'a': '13.500'}, '13.500'), |
|
111
|
|
|
({'a': Decimal('13.500')}, '13.500'), |
|
112
|
|
|
({'a': 1.1 + 2.2}, '3.3'), |
|
113
|
|
|
] |
|
114
|
|
|
|
|
115
|
|
|
multiplier = 2 ** precision |
|
116
|
|
|
for (in_val, out_val) in test_values: |
|
117
|
|
|
ret = elem.pack(in_val) |
|
118
|
|
|
|
|
119
|
|
|
if not isinstance(out_val, Decimal): |
|
120
|
|
|
out_val = Decimal(out_val) |
|
121
|
|
|
|
|
122
|
|
|
self.assertEqual(ret, int((out_val * multiplier)).to_bytes(4, 'big')) |
|
123
|
|
|
|
|
124
|
|
|
def test_valid_make(self): |
|
125
|
|
|
"""Test full packing.""" |
|
126
|
|
|
my_message = Message('my_msg', [ |
|
127
|
|
|
('my_fixed', 'F', 'i', 8), |
|
128
|
|
|
('not_specified_fixed', 'F', 'i', 8), |
|
129
|
|
|
('other_fixed', 'F', 'i', 8, 3), |
|
130
|
|
|
('just_a_num', 'i'), |
|
131
|
|
|
('a_string', '32s'), |
|
132
|
|
|
('this_fixed', 'F', 'i', 5), |
|
133
|
|
|
], Mode.Big) |
|
134
|
|
|
|
|
135
|
|
|
data = { |
|
136
|
|
|
'my_fixed': 1.1 + 2.2, |
|
137
|
|
|
'other_fixed': Decimal('1.1') + Decimal('2.2'), |
|
138
|
|
|
'not_specified_fixed': Decimal('1.1') + Decimal('2.2'), |
|
139
|
|
|
'just_a_num': 16, |
|
140
|
|
|
'a_string': '=====================', |
|
141
|
|
|
'this_fixed': '1.9375', |
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
packed = my_message.pack(data) |
|
145
|
|
|
unpacked = my_message.unpack(packed) |
|
146
|
|
|
|
|
147
|
|
|
assert unpacked.a_string == data['a_string'] |
|
148
|
|
|
assert unpacked.just_a_num == data['just_a_num'] |
|
149
|
|
|
assert unpacked.other_fixed == Decimal('3.3') |
|
150
|
|
|
assert unpacked.my_fixed == Decimal('3.296875') |
|
151
|
|
|
assert unpacked.not_specified_fixed == Decimal('3.296875') |
|
152
|
|
|
assert unpacked.this_fixed == Decimal(data['this_fixed']) |
|
153
|
|
|
|