Coverage for tests/comm/test_cstruct.py: 100%

63 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-07-10 13:43 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2021 RSK Labs Ltd 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining a copy of 

6# this software and associated documentation files (the "Software"), to deal in 

7# the Software without restriction, including without limitation the rights to 

8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 

9# of the Software, and to permit persons to whom the Software is furnished to do 

10# so, subject to the following conditions: 

11# 

12# The above copyright notice and this permission notice shall be included in all 

13# copies or substantial portions of the Software. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 

21# SOFTWARE. 

22 

23from unittest import TestCase 

24from parameterized import parameterized 

25from comm.cstruct import CStruct 

26 

27import logging 

28 

29logging.disable(logging.CRITICAL) 

30 

31 

32class RandomBisStruct(CStruct): 

33 """ 

34 random_bis_t 

35 

36 uint16_t another_double 

37 uint8_t single_arr 10 

38 uint32_t another_quad 

39 """ 

40 

41 

42class RandomTrisStruct(CStruct): 

43 """ 

44 random_tris_t 

45 

46 uint8_t arr_one 2 

47 uint8_t arr_two 3 

48 """ 

49 

50 

51class RandomStruct(CStruct): 

52 """ 

53 random_t 

54 

55 uint8_t single_val 

56 uint16_t double_val 

57 uint32_t quad_val 

58 uint64_t oct_val 

59 

60 random_bis_t other_random 

61 random_tris_t yet_other_random 

62 """ 

63 

64 

65class Invalid1(CStruct): 

66 """ 

67 invalid_1 

68 

69 nonexistent_type something 

70 """ 

71 

72 

73class Invalid2(CStruct): 

74 """ 

75 invalid_2 

76 

77 uint32_t withlength 5 

78 """ 

79 

80 

81class ValidWithInvalid(CStruct): 

82 """ 

83 valid_with_invalid 

84 

85 uint8_t a_number 

86 uint16_t another_number 

87 invalid_2 something_invalid 

88 """ 

89 

90 

91class TestCStruct(TestCase): 

92 def setUp(self): 

93 self.packed = bytes.fromhex( 

94 "99" # single_val 

95 "0102" # double_val 

96 "03040506" # quad_val 

97 "0708090a0b0c0d0e" # oct_val 

98 "8899" # another_double 

99 "00112233445566778899" # single_arr 

100 "d1d2d3d4" # another_quad 

101 "aabb" # arr_one 

102 "ccddee" # arr_two 

103 ) 

104 

105 def test_expected_sizes(self): 

106 self.assertEqual(16, RandomBisStruct.get_bytelength()) 

107 self.assertEqual(5, RandomTrisStruct.get_bytelength()) 

108 self.assertEqual(15 + 

109 RandomBisStruct.get_bytelength() + 

110 RandomTrisStruct.get_bytelength(), 

111 RandomStruct.get_bytelength()) 

112 

113 def test_parsing_default(self): 

114 parsed = RandomStruct(self.packed) 

115 

116 self.assertEqual(0x99, parsed.single_val) 

117 self.assertEqual(0x0201, parsed.double_val) 

118 self.assertEqual(0x06050403, parsed.quad_val) 

119 self.assertEqual(0x0e0d0c0b0a090807, parsed.oct_val) 

120 

121 self.assertEqual(0x9988, parsed.other_random.another_double) 

122 self.assertEqual(bytes.fromhex("00112233445566778899"), 

123 parsed.other_random.single_arr) 

124 self.assertEqual(0xd4d3d2d1, parsed.other_random.another_quad) 

125 

126 self.assertEqual(bytes.fromhex("aabb"), parsed.yet_other_random.arr_one) 

127 self.assertEqual(bytes.fromhex("ccddee"), parsed.yet_other_random.arr_two) 

128 

129 self.assertEqual({ 

130 "single_val": 0x99, 

131 "double_val": 0x0201, 

132 "quad_val": 0x06050403, 

133 "oct_val": 0x0e0d0c0b0a090807, 

134 "other_random": { 

135 "another_double": 0x9988, 

136 "single_arr": "00112233445566778899", 

137 "another_quad": 0xd4d3d2d1, 

138 }, 

139 "yet_other_random": { 

140 "arr_one": "aabb", 

141 "arr_two": "ccddee", 

142 } 

143 }, parsed.to_dict()) 

144 

145 def test_parsing_little_offset(self): 

146 parsed = RandomStruct(b"thisisrandom" + self.packed, offset=12, little=True) 

147 

148 self.assertEqual(0x99, parsed.single_val) 

149 self.assertEqual(0x0201, parsed.double_val) 

150 self.assertEqual(0x06050403, parsed.quad_val) 

151 self.assertEqual(0x0e0d0c0b0a090807, parsed.oct_val) 

152 

153 self.assertEqual(0x9988, parsed.other_random.another_double) 

154 self.assertEqual(bytes.fromhex("00112233445566778899"), 

155 parsed.other_random.single_arr) 

156 self.assertEqual(0xd4d3d2d1, parsed.other_random.another_quad) 

157 

158 self.assertEqual(bytes.fromhex("aabb"), parsed.yet_other_random.arr_one) 

159 self.assertEqual(bytes.fromhex("ccddee"), parsed.yet_other_random.arr_two) 

160 

161 self.assertEqual({ 

162 "single_val": 0x99, 

163 "double_val": 0x0201, 

164 "quad_val": 0x06050403, 

165 "oct_val": 0x0e0d0c0b0a090807, 

166 "other_random": { 

167 "another_double": 0x9988, 

168 "single_arr": "00112233445566778899", 

169 "another_quad": 0xd4d3d2d1, 

170 }, 

171 "yet_other_random": { 

172 "arr_one": "aabb", 

173 "arr_two": "ccddee", 

174 } 

175 }, parsed.to_dict()) 

176 

177 def test_parsing_big(self): 

178 parsed = RandomStruct(self.packed, little=False) 

179 

180 self.assertEqual(0x99, parsed.single_val) 

181 self.assertEqual(0x0102, parsed.double_val) 

182 self.assertEqual(0x03040506, parsed.quad_val) 

183 self.assertEqual(0x0708090a0b0c0d0e, parsed.oct_val) 

184 

185 self.assertEqual(0x8899, parsed.other_random.another_double) 

186 self.assertEqual(bytes.fromhex("00112233445566778899"), 

187 parsed.other_random.single_arr) 

188 self.assertEqual(0xd1d2d3d4, parsed.other_random.another_quad) 

189 

190 self.assertEqual(bytes.fromhex("aabb"), parsed.yet_other_random.arr_one) 

191 self.assertEqual(bytes.fromhex("ccddee"), parsed.yet_other_random.arr_two) 

192 

193 self.assertEqual({ 

194 "single_val": 0x99, 

195 "double_val": 0x0102, 

196 "quad_val": 0x03040506, 

197 "oct_val": 0x0708090a0b0c0d0e, 

198 "other_random": { 

199 "another_double": 0x8899, 

200 "single_arr": "00112233445566778899", 

201 "another_quad": 0xd1d2d3d4, 

202 }, 

203 "yet_other_random": { 

204 "arr_one": "aabb", 

205 "arr_two": "ccddee", 

206 } 

207 }, parsed.to_dict()) 

208 

209 def test_parsing_toosmall(self): 

210 with self.assertRaises(ValueError): 

211 RandomStruct(b"thisistoosmall") 

212 

213 @parameterized.expand([ 

214 ("invalid_one", Invalid1), 

215 ("invalid_two", Invalid2), 

216 ("valid_with_invalid", ValidWithInvalid) 

217 ]) 

218 def test_invalid_spec(self, _, kls): 

219 with self.assertRaises(ValueError): 

220 kls.get_bytelength() 

221 

222 with self.assertRaises(ValueError): 

223 kls(b'somethingtoparse')