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
|
|
|
|