Coverage for test/d7a/serial_modem_interface/test_parser.py: 97%
79 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-24 08:03 +0200
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-24 08:03 +0200
1#
2# Copyright (c) 2015-2021 University of Antwerp, Aloxy NV.
3#
4# This file is part of pyd7a.
5# See https://github.com/Sub-IoT/pyd7a for further info.
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
20# unit tests for the serial interface parser
22import unittest
23from d7a.support.Crc import calculate_crc
25from d7a.serial_modem_interface.parser import Parser
27class TestParser(unittest.TestCase):
28 def setUp(self):
29 self.parser = Parser()
30 self.counter = 0
32 def test_basic_valid_message(self):
33 self.counter = self.counter + 1
34 alp_cmd_bytes = [
35 0x20, # action=32/ReturnFileData
36 0x40, # File ID
37 0x00, # offset
38 0x04, # length
39 0x00, 0xf3, 0x00, 0x00 # data
40 ]
41 # calculate crc
42 crc_array = calculate_crc(bytearray(alp_cmd_bytes))
44 # |sync|sync|counter|message type|length|crc1|crc2|
45 frame = [
46 0xC0, # interface sync byte
47 0, # interface version
48 self.counter, # counter
49 0x01, # message type,
50 len(alp_cmd_bytes), # ALP cmd length
51 crc_array[0], crc_array[1] # ALP crc
53 ] + alp_cmd_bytes
55 (message_type, cmds, info) = self.parser.parse(frame)
56 self.assertEqual(cmds[0].actions[0].operation.op, 32)
57 self.assertEqual(cmds[0].actions[0].operation.operand.length.value, 4)
59 def test_bad_identifier(self):
60 (message_type, cmds, info) = self.parser.parse([
61 0x0c, # that's 0c not c0 ! ;-)
62 0x04, 0x00, 0x00, 0x00,
63 0x20,
64 0x24, 0x8a, 0xb6, 0x00, 0x52, 0x0b, 0x35, 0x2c,
65 0x20,
66 0x40,
67 0x00,
68 0x00
69 ])
70 self.assertEqual(len(cmds), 0)
71 self.assertEqual(len(info["errors"]), 1)
73 def test_buffer_skipping(self):
74 self.parser.buffer = bytearray([ 0x10, 0x20, 0x30, 0xc0, 0x10, 0x20, 0x30 ])
75 skipped = self.parser.skip_bad_buffer_content()
76 self.assertEqual(skipped, 3)
77 self.assertEqual(self.parser.buffer, bytearray([ 0xc0, 0x10, 0x20, 0x30 ]))
79 def test_entire_buffer_skipping(self):
80 self.parser.buffer = [ 0x10, 0x20, 0x30, 0x10, 0x20, 0x30 ]
81 skipped = self.parser.skip_bad_buffer_content()
82 self.assertEqual(skipped, 6)
83 self.assertEqual(self.parser.buffer, bytearray())
85 def test_empty_buffer_skipping(self):
86 self.parser.buffer = []
87 skipped = self.parser.skip_bad_buffer_content()
88 self.assertEqual(skipped, 0)
89 self.assertEqual(self.parser.buffer, [])
91 def test_buffer_skipping_with_first_item_the_id(self):
92 self.parser.buffer = [ 0xc0, 0x10, 0x20, 0x30 ]
93 skipped = self.parser.skip_bad_buffer_content()
94 self.assertEqual(skipped, 4)
95 self.assertEqual(self.parser.buffer, bytearray())
97 def test_buffer_skipping_with_first_and_second_item_the_id(self):
98 self.parser.buffer = bytearray([ 0xc0, 0xc0, 0x10, 0x20, 0x30 ])
99 skipped = self.parser.skip_bad_buffer_content()
100 self.assertEqual(skipped, 1)
101 self.assertEqual(self.parser.buffer, bytearray([ 0xc0, 0x10, 0x20, 0x30 ]))
103 # |sync|sync|counter|message type|length|crc1|crc2|
104 def test_bad_identifier_with_identifier_in_body(self):
105 self.counter = self.counter + 1
106 (message_type, cmds, info) = self.parser.parse(bytearray([
107 0x0c, # that's 0c not c0 ! ;-)
108 0x04, 0x00, 0x00, 0x00,
109 0x20,
110 0x24, 0x8a,
111 0xc0, 0x00, # here's another one
112 self.counter, 0X01, 7, 0x33, 0xAD, # calculated crc beforehand
113 0x0b, 0x35, 0x2c,
114 0x7d,
115 0x40,
116 0x00,
117 0x00
118 ]))
119 self.assertEqual(len(cmds), 0)
120 self.assertEqual(len(info["errors"]), 2)
121 self.assertEqual(info["errors"][0]["skipped"], 8)
123 def test_partial_command(self):
124 self.counter = self.counter + 1
125 alp_action_bytes = bytearray([
126 0x20, # action=32/ReturnFileData
127 0x40, # File ID
128 0x00, # offset
129 0x04, # length
130 0x00, 0xf3, 0x00, 0x00 # data
131 ])
133 (message_type, cmds, info) = self.parser.parse(bytearray([
134 0xc0, # interface start
135 0,
136 self.counter, 0x01, 2 * len(alp_action_bytes), 0xDC, 0xF8 # expect 2 ALP actions but only one in buffer
137 ]) + alp_action_bytes)
138 self.assertEqual(len(cmds), 0)
139 self.assertEqual(len(info["errors"]), 0)
140 self.assertEqual(info["parsed"], 0)
142 def test_continue_partial_command(self): # ?
143 self.test_partial_command() # incomplete command, add second ALP action to complete it ...
144 (message_type, cmds, info) = self.parser.parse(bytearray([
145 0x20, # action=32/ReturnFileData
146 0x40, # File ID
147 0x00, # offset
148 0x04, # length
149 0x00, 0xf3, 0x00, 0x00 # data
150 ]))
151 self.assertEqual(len(info["errors"]), 0)
152 self.assertEqual(len(cmds[0].actions), 2)
155 def test_continue_from_bad_buffer(self):
156 self.test_bad_identifier_with_identifier_in_body() # buffer is bad now
157 self.test_basic_valid_message() # cont. with valid msg
160 def test_continue_partial_second_frame(self):
161 self.counter = self.counter + 1
162 alp_cmd_bytes = [
163 0x20, # action=32/ReturnFileData
164 0x40, # File ID
165 0x00, # offset
166 0x04, # length
167 0x00, 0xf3, 0x00, 0x00 # data
168 ]
170 frame = [
171 0xC0, # interface sync byte
172 0, # interface version
173 self.counter, 0x01, len(alp_cmd_bytes), 0xA4, 0xBE, # calculated crc beforehand
174 ] + alp_cmd_bytes + [
175 # second, partial frame
176 0xC0, # interface sync byte
177 0, # interface version
178 self.counter + 1, 0x01, len(alp_cmd_bytes), 0xA4, 0xBE, # calculated crc beforehand
179 ]
180 self.counter = self.counter + 1
181 # first frame should parse
182 (message_type, cmds, info) = self.parser.parse(frame)
183 self.assertEqual(cmds[0].actions[0].operation.op, 32)
184 self.assertEqual(cmds[0].actions[0].operation.operand.length.value, 4)
186 # and now complete the second frame and check this is parsed as well
187 (message_type, cmds, info) = self.parser.parse(alp_cmd_bytes) # the missing bytes only, without the frame header
188 self.assertEqual(cmds[0].actions[0].operation.op, 32)
189 self.assertEqual(cmds[0].actions[0].operation.operand.length.value, 4)
192if __name__ == '__main__':
193 suite = unittest.TestLoader().loadTestsFromTestCase(TestParser)
194 unittest.TextTestRunner(verbosity=2).run(suite)