Coverage for tests/test_xmlparser/test_types.py: 100%

259 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-21 15:58 +0200

1from lxml.etree import XML as xml 

2from sbe2.xmlparser.types import parse_valid_value, parse_enum, parse_choice, parse_set, parse_ref, parse_composite, parse_type, parse_message_schema, parse_message, parse_field, parse_data, parse_group 

3from sbe2.xmlparser.errors import SchemaParsingError 

4from sbe2.xmlparser.ctx import ParsingContext 

5from pytest import raises 

6from sbe2.schema import Type, Enum, Choice, Set, Ref, ValidValue, Composite, Presence, MessageSchema, ByteOrder, primitive_type 

7from sbe2.schema import builtin 

8 

9 

10 

11def test_parse_valid_value(): 

12 node = xml( 

13 '<validValue name="something" sinceVersion="5" deprecated="8" description="blah" >5</validValue>' 

14 ) 

15 vv = parse_valid_value(node, 'int') 

16 assert vv.name == "something" 

17 assert vv.value == 5 

18 assert vv.since_version == 5 

19 assert vv.deprecated == 8 

20 assert vv.description == "blah" 

21 

22 

23def test_parse_enum(): 

24 node = xml( 

25 """ 

26 <enum name="TestEnum" sinceVersion="1" deprecated="2" description="Test Enum" encodingType="int" offset="0"> 

27 <validValue name="Value1">1</validValue> 

28 <validValue name="Value2">2</validValue> 

29 </enum> 

30 """ 

31 ) 

32 enum = parse_enum(node) 

33 assert enum.name == "TestEnum" 

34 assert enum.since_version == 1 

35 assert enum.deprecated == 2 

36 assert enum.description == "Test Enum" 

37 assert enum.encoding_type_name == 'int' 

38 assert enum.offset == 0 

39 assert len(enum.valid_values) == 2 

40 assert enum.valid_values[0].name == "Value1" 

41 assert enum.valid_values[0].value == 1 

42 assert enum.valid_values[1].name == "Value2" 

43 assert enum.valid_values[1].value == 2 

44 

45 # duplicate names and values should raise an error 

46 with raises(SchemaParsingError): 

47 node = xml( 

48 """ 

49 <enum name="TestEnum" sinceVersion="1" deprecated="2" description="Test Enum" encodingType="int" offset="0"> 

50 <validValue name="Value1">2</validValue> 

51 <validValue name="Value2">2</validValue> 

52 </enum> 

53 """ 

54 ) 

55 parse_enum(node) 

56 

57 with raises(SchemaParsingError): 

58 node = xml( 

59 """ 

60 <enum name="TestEnum" sinceVersion="1" deprecated="2" description="Test Enum" encodingType="int" offset="0"> 

61 <validValue name="Value1">1</validValue> 

62 <validValue name="Value1">2</validValue> 

63 </enum> 

64 """ 

65 ) 

66 parse_enum(node) 

67 

68 

69def test_parse_choice(): 

70 node = xml( 

71 """ 

72 <choice name="TestChoice" sinceVersion="1" deprecated="2" description="Test Choice">6</choice> 

73 """ 

74 ) 

75 choice = parse_choice(node) 

76 assert choice.name == "TestChoice" 

77 assert choice.since_version == 1 

78 assert choice.deprecated == 2 

79 assert choice.description == "Test Choice" 

80 assert choice.value == 6 

81 

82 with raises(SchemaParsingError): 

83 node = xml( 

84 """ 

85 <choice name="TestChoice" sinceVersion="1" deprecated="2" description="Test Choice">invalid</choice> 

86 """ 

87 ) 

88 parse_choice(node) 

89 

90 

91def test_parse_set(): 

92 node = xml( 

93 """ 

94 <set name="TestSet" sinceVersion="1" deprecated="2" description="Test Set" encodingType="int" offset="0"> 

95 <choice name="Value1">1</choice> 

96 <choice name="Value2">2</choice> 

97 </set> 

98 """ 

99 ) 

100 set_ = parse_set(node) 

101 assert set_.name == "TestSet" 

102 assert set_.since_version == 1 

103 assert set_.deprecated == 2 

104 assert set_.description == "Test Set" 

105 assert set_.encoding_type_name == 'int' 

106 assert set_.offset == 0 

107 assert len(set_.choices) == 2 

108 assert set_.choices[0].name == "Value1" 

109 assert set_.choices[0].value == 1 

110 assert set_.choices[1].name == "Value2" 

111 assert set_.choices[1].value == 2 

112 

113 # duplicate names and values should raise an error 

114 

115 with raises(SchemaParsingError): 

116 node = xml( 

117 """ 

118 <set name="TestSet" sinceVersion="1" deprecated="2" description="Test Set" encodingType="int" offset="0"> 

119 <choice name="Value1">2</choice> 

120 <choice name="Value2">2</choice> 

121 </set> 

122 """ 

123 ) 

124 parse_set(node) 

125 with raises(SchemaParsingError): 

126 node = xml( 

127 """ 

128 <set name="TestSet" sinceVersion="1" deprecated="2" description="Test Set" encodingType="int" offset="0"> 

129 <choice name="Value1">1</choice> 

130 <choice name="Value1">2</choice> 

131 </set> 

132 """ 

133 ) 

134 parse_set(node) 

135 

136 

137def test_parse_ref(): 

138 node = xml( 

139 """ 

140 <ref name="TestRef" description="Test Reference" type="int" offset="56"/> 

141 """ 

142 ) 

143 ref = parse_ref(node) 

144 assert ref.name == "TestRef" 

145 assert ref.description == "Test Reference" 

146 assert ref.type_name == 'int' 

147 assert ref.offset == 56 

148 

149 with raises(SchemaParsingError): 

150 node = xml( 

151 """ 

152 <ref name="TestRef" sinceVersion="1" deprecated="2" description="Test Reference"/> 

153 """ 

154 ) 

155 parse_ref(node) # type should be mandatory 

156 

157 

158def test_parse_composite(): 

159 node = xml( 

160 """ 

161 <composite name="TestComposite" sinceVersion="1" deprecated="2" description="Test Composite" offset="0"> 

162 <type name="Field1" primitiveType="int" offset="0"/> 

163 <set name="Field2" encodingType="int" offset="4"> 

164 <choice name="Choice1">1</choice> 

165 <choice name="Choice2">2</choice> 

166 </set> 

167 <enum name="Field3" encodingType="int" offset="8"> 

168 <validValue name="Value1">1</validValue> 

169 <validValue name="Value2">2</validValue> 

170 </enum> 

171 <ref name="Field4" description="Reference Field" type="decimal" offset="12"/> 

172 <composite name="NestedComposite" sinceVersion="1" deprecated="2" description="Nested Composite"> 

173 <type name="NestedField" primitiveType="float" offset="0"/> 

174 </composite> 

175 </composite> 

176 """ 

177 ) 

178 composite = parse_composite(node) 

179 assert composite.name == "TestComposite" 

180 assert composite.since_version == 1 

181 assert composite.deprecated == 2 

182 assert composite.description == "Test Composite" 

183 assert composite.offset == 0 

184 assert len(composite.elements) == 5 

185 assert type(composite.elements[0]) == Type 

186 assert composite.elements[0].name == "Field1" 

187 assert composite.elements[0].primitive_type is primitive_type.int_ 

188 assert composite.elements[0].offset == 0 

189 assert type(composite.elements[1]) == Set 

190 assert composite.elements[1].name == "Field2" 

191 assert composite.elements[1].encoding_type_name == 'int' 

192 assert composite.elements[1].offset == 4 

193 assert len(composite.elements[1].choices) == 2 

194 assert composite.elements[1].choices[0].name == "Choice1" 

195 assert composite.elements[1].choices[0].value == 1 

196 assert composite.elements[1].choices[1].name == "Choice2" 

197 assert composite.elements[1].choices[1].value == 2 

198 assert composite.elements[2].name == "Field3" 

199 assert type(composite.elements[2]) == Enum 

200 assert composite.elements[2].encoding_type_name == 'int' 

201 assert composite.elements[2].offset == 8 

202 assert len(composite.elements[2].valid_values) == 2 

203 assert composite.elements[2].valid_values[0].name == "Value1" 

204 assert composite.elements[2].valid_values[0].value == 1 

205 assert composite.elements[2].valid_values[1].name == "Value2" 

206 assert composite.elements[2].valid_values[1].value == 2 

207 assert type(composite.elements[3]) == Ref 

208 assert composite.elements[3].name == "Field4" 

209 assert composite.elements[3].description == "Reference Field" 

210 assert composite.elements[3].type_name == 'decimal' 

211 assert composite.elements[3].offset == 12 

212 assert type(composite.elements[4]) == Composite 

213 assert composite.elements[4].name == "NestedComposite" 

214 assert composite.elements[4].since_version == 1 

215 assert composite.elements[4].deprecated == 2 

216 assert composite.elements[4].description == "Nested Composite" 

217 assert len(composite.elements[4].elements) == 1 

218 assert type(composite.elements[4].elements[0]) == Type 

219 assert composite.elements[4].elements[0].name == "NestedField" 

220 assert composite.elements[4].elements[0].primitive_type is primitive_type.float_ 

221 assert composite.elements[4].elements[0].offset == 0 

222 

223 

224def test_parse_type(): 

225 node = xml( 

226 """ 

227 <type name="TestType" primitiveType="int" sinceVersion="1" deprecated="2" description="Test Type" offset="0" presence="required" length="3" /> 

228 """ 

229 ) 

230 type_ = parse_type(node) 

231 assert type_.name == "TestType" 

232 assert type_.primitive_type is primitive_type.int_ 

233 assert type_.since_version == 1 

234 assert type_.deprecated == 2 

235 assert type_.description == "Test Type" 

236 assert type_.offset == 0 

237 assert type_.presence == Presence.REQUIRED 

238 assert type_.length == 3 

239 assert type_.const_val is None 

240 assert type_.value_ref is None 

241 assert type_.total_length == 4 

242 

243 

244 with raises(SchemaParsingError): 

245 node = xml( 

246 """ 

247 <type name="TestType" sinceVersion="1" deprecated="2" description="Test Type"/> 

248 """ 

249 ) 

250 parse_type(node) # primitiveType should be mandatory 

251 

252 

253def test_parse_type_const_value(): 

254 node = xml( 

255 """ 

256 <type name="TestType" primitiveType="int" sinceVersion="1" deprecated="2" description="Test Type" offset="0" presence="constant">5</type> 

257 """ 

258 ) 

259 type_ = parse_type(node) 

260 assert type_.presence == Presence.CONSTANT 

261 assert type_.value == '5' 

262 assert type_.const_val is None 

263 assert type_.value_ref is None 

264 

265 ctx = ParsingContext() 

266 type_.lazy_bind(ctx.types) 

267 assert type_.const_val == 5 

268 

269 

270def test_parse_type_value_ref(): 

271 node = xml( 

272 """ 

273 <type name="TestType" primitiveType="int" sinceVersion="1" deprecated="2" description="Test Type" offset="0" presence="constant" valueRef="Example.Something"/> 

274 """ 

275 ) 

276 type_ = parse_type(node) 

277 assert type_.presence == Presence.CONSTANT 

278 assert type_.value is None 

279 assert type_.const_val is None 

280 assert type_.value_ref == "Example.Something" 

281 

282 ctx = ParsingContext() 

283 enum = Enum(name="Example", description='', encoding_type_name='int', valid_values=[ 

284 ValidValue(name="Something", description='', value=5), 

285 ValidValue(name="Nothing", description='', value=7), 

286 ]) 

287 ctx.types.add(enum) 

288 type_.lazy_bind(ctx.types) 

289 assert type_.const_val == 5 

290 

291 

292 

293def test_parse_message_schema(): 

294 node = xml( 

295 """ 

296 <messageSchema version="1" package="test.package" headerType="myHeader" byteOrder="bigEndian" id="123" semanticVersion="1.0.0"> 

297 </messageSchema> 

298 """ 

299 ) 

300 ms = parse_message_schema(node) 

301 assert ms.version == 1 

302 assert ms.package == "test.package" 

303 assert ms.header_type == "myHeader" 

304 assert ms.byte_order == ByteOrder.BIG_ENDIAN 

305 assert ms.id == 123 

306 assert ms.semantic_version == "1.0.0" 

307 

308 

309def test_parse_message_attributes(): 

310 node = xml( 

311 """ 

312 <message id="1" name="TestMessage" description="This is a test message" semanticType="test" blockLength="8" sinceVersion="1" deprecated="2" alignment="4"> 

313 </message> 

314 """ 

315 ) 

316 ctx = ParsingContext() 

317 message = parse_message(node, ctx, 'package') 

318 assert message.id == 1 

319 assert message.name == "TestMessage" 

320 assert message.description == "This is a test message" 

321 assert message.semantic_type == "test" 

322 assert message.block_length == 8 

323 assert message.since_version == 1 

324 assert message.deprecated == 2 

325 assert message.alignment == 4 

326 assert message.fields == [] 

327 assert message.groups == [] 

328 assert message.datas == [] 

329 assert message.package == 'package' 

330 

331 

332def test_parse_message_elements(): 

333 node = xml( 

334 """ 

335 <message id="1" name="TestMessage"> 

336 <field id="2" name="Field1" type="int"/> 

337 <group id="3" name="SubGroup" dimensionType="int"/> 

338 <data id="4" name="Data1" type="int"/> 

339 </message> 

340 """ 

341 ) 

342 ctx = ParsingContext() 

343 message = parse_message(node, ctx, 'package') 

344 assert len(message.fields) == 1 

345 assert message.fields[0].id == 2 

346 assert message.fields[0].name == "Field1" 

347 assert message.fields[0].type == builtin.int_ 

348 assert len(message.groups) == 1 

349 assert message.groups[0].id == 3 

350 assert message.groups[0].name == "SubGroup" 

351 assert message.groups[0].dimension_type == builtin.int_ 

352 assert len(message.datas) == 1 

353 assert message.datas[0].id == 4 

354 assert message.datas[0].name == "Data1" 

355 assert message.datas[0].type_ == builtin.int_ 

356 assert message.package == 'package' 

357 

358def test_parse_field(): 

359 node = xml( 

360 """ 

361 <field id="1" name="TestField" description="This is a test field" type="int" offset="0" alignment="4" presence="required" sinceVersion="1" deprecated="2" /> 

362 """ 

363 ) 

364 ctx = ParsingContext() 

365 field = parse_field(node, ctx) 

366 assert field.id == 1 

367 assert field.name == "TestField" 

368 assert field.description == "This is a test field" 

369 assert field.type == builtin.int_ 

370 assert field.offset == 0 

371 assert field.alignment == 4 

372 assert field.presence == Presence.REQUIRED 

373 assert field.since_version == 1 

374 assert field.deprecated == 2 

375 assert field.value_ref is None 

376 assert field.constant_value is None 

377 

378 

379def test_parse_data(): 

380 node = xml( 

381 """ 

382 <data id="123" name="TestData" description="This is a test data" type="int" semanticType="text" sinceVersion="1" deprecated="2" /> 

383 """ 

384 ) 

385 ctx = ParsingContext() 

386 data = parse_data(node, ctx) 

387 assert data.id == 123 

388 assert data.name == "TestData" 

389 assert data.description == "This is a test data" 

390 assert data.type_ == builtin.int_ 

391 assert data.semantic_type == "text" 

392 assert data.since_version == 1 

393 assert data.deprecated == 2 

394 

395 

396def test_parse_group_attributes(): 

397 node = xml( 

398 """ 

399 <group name="TestGroup" id="1" description="This is a test group" semanticType="test" blockLength="8" sinceVersion="1" deprecated="2" dimensionType="int"> 

400 </group> 

401 """ 

402 ) 

403 ctx = ParsingContext() 

404 group = parse_group(node, ctx) 

405 assert group.name == "TestGroup" 

406 assert group.id == 1 

407 assert group.description == "This is a test group" 

408 assert group.block_length == 8 

409 assert len(group.fields) == 0 

410 assert len(group.groups) == 0 

411 assert len(group.datas) == 0 

412 assert group.dimension_type == builtin.int_ 

413 assert group.since_version == 1 

414 assert group.deprecated == 2 

415 

416def test_parse_group_elements(): 

417 node = xml( 

418 """ 

419 <group name="TestGroup" id="1" dimensionType="int"> 

420 <field id="2" name="Field1" type="int"/> 

421 <group id="3" name="SubGroup" dimensionType="int"/> 

422 <data id="4" name="Data1" type="int"/> 

423 </group> 

424 """ 

425 ) 

426 ctx = ParsingContext() 

427 group = parse_group(node, ctx) 

428 assert len(group.fields) == 1 

429 assert group.fields[0].id == 2 

430 assert group.fields[0].name == "Field1" 

431 assert group.fields[0].type == builtin.int_ 

432 assert len(group.groups) == 1 

433 assert group.groups[0].id == 3 

434 assert group.groups[0].name == "SubGroup" 

435 assert group.groups[0].dimension_type == builtin.int_ 

436 assert len(group.datas) == 1 

437 assert group.datas[0].id == 4 

438 assert group.datas[0].name == "Data1" 

439 assert group.datas[0].type_ == builtin.int_