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

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# 

19 

20# unit tests for the serial interface parser 

21 

22import unittest 

23from d7a.support.Crc import calculate_crc 

24 

25from d7a.serial_modem_interface.parser import Parser 

26 

27class TestParser(unittest.TestCase): 

28 def setUp(self): 

29 self.parser = Parser() 

30 self.counter = 0 

31 

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

43 

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 

52 

53 ] + alp_cmd_bytes 

54 

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) 

58 

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) 

72 

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

78 

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

84 

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, []) 

90 

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

96 

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

102 

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) 

122 

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

132 

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) 

141 

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) 

153 

154 

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 

158 

159 

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 ] 

169 

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) 

185 

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) 

190 

191 

192if __name__ == '__main__': 

193 suite = unittest.TestLoader().loadTestsFromTestCase(TestParser) 

194 unittest.TextTestRunner(verbosity=2).run(suite)