1# !/usr/bin/env python3 2# coding=utf-8 3""" 4* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 5* 6* HDF is dual licensed: you can use it either under the terms of 7* the GPL, or the BSD license, at your option. 8* See the LICENSE file in the root of this repository for complete details. 9""" 10import json 11import sys 12import unittest 13from os import remove 14 15import CppHeaderParser 16 17sys.path.insert(0, '..') 18 19try: 20 from _header_parser import HeaderParser 21finally: 22 pass 23 24 25class HeaderParserTestCase(unittest.TestCase): 26 27 def test_extract_path_and_file(self): 28 parser = HeaderParser() 29 30 parser._extract_path_and_file('include/test/audio_manager.h') 31 self.assertEqual(parser._header_dict.get('path'), 'include/test') 32 self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') 33 34 parser._extract_path_and_file('./include/test/audio_manager.h') 35 self.assertEqual(parser._header_dict.get('path'), './include/test') 36 self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') 37 38 parser._extract_path_and_file('../include/test/audio_manager.h') 39 self.assertEqual(parser._header_dict.get('path'), '../include/test') 40 self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') 41 42 parser._extract_path_and_file('\\include\\test\\audio_manager.h') 43 self.assertEqual(parser._header_dict.get('path'), '\\include\\test') 44 self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') 45 46 parser._extract_path_and_file('d:\\include\\test\\audio_manager.h') 47 self.assertEqual(parser._header_dict.get('path'), 'd:\\include\\test') 48 self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') 49 50 def test_extract_include(self): 51 parser = HeaderParser() 52 parser._extract_include(['"buffer_handle.h"', '"buffer_type.h"']) 53 self.assertSequenceEqual(parser._header_dict.get("import"), ['buffer_handle.h', 'buffer_type.h']) 54 55 def test_extract_include_system_file(self): 56 parser = HeaderParser() 57 parser._extract_include(['<fcntl.h>', '<sys/types.h>', '"buffer_handle.h"', '"buffer_type.h"']) 58 self.assertSequenceEqual(parser._header_dict.get("import"), ['buffer_handle.h', 'buffer_type.h']) 59 60 def test_extract_enum(self): 61 header_file = """ 62 typedef enum { 63 DISPLAY_SUCCESS = 0, 64 DISPLAY_FAILURE = 1, 65 } DispErrCode; 66 """ 67 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 68 parser = HeaderParser() 69 parser._extract_enum(hjson["enums"]) 70 self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'DispErrCode', 71 'members': [{'name': 'DISPLAY_SUCCESS', 'value': 0}, 72 {'name': 'DISPLAY_FAILURE', 73 'value': 1}]}]) 74 75 def test_extract_enum_with_type(self): 76 header_file = """ 77 enum CamRetCode : int32_t { 78 NO_ERROR = 0, 79 CAMERA_BUSY = 1, 80 }; 81 """ 82 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 83 parser = HeaderParser() 84 parser._extract_enum(hjson["enums"]) 85 self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'CamRetCode', 86 'members': [{'name': 'NO_ERROR', 'value': 0}, 87 {'name': 'CAMERA_BUSY', 'value': 1}]}]) 88 89 def test_extract_enum_unexpected_value(self): 90 header_file = """ 91 typedef enum { 92 DISPLAY_SUCCESS = 0x1234u, 93 DISPLAY_FAILURE = -1, 94 HBM_USE_CPU_READ = (1 << 0), 95 } DispErrCode; 96 """ 97 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 98 hjson["enums"][0]["filename"] = "test_header_parser.py" 99 hjson["enums"][0]["line_number"] = 0 100 parser = HeaderParser() 101 parser._extract_enum(hjson["enums"]) 102 self.assertSequenceEqual( 103 parser._header_dict.get("enum"), [{'name': 'DispErrCode', 104 'members': [ 105 {'name': 'DISPLAY_SUCCESS', 106 'value': '0x1234 u // unexpected \'0x\''}, 107 {'name': 'DISPLAY_FAILURE', 'value': '- 1 // unexpected \'-\''}, 108 {'name': 'HBM_USE_CPU_READ', 109 'value': '( 1 << 0 ) // unexpected \'(\''} 110 ]}]) 111 112 def test_extract_enum_without_name(self): 113 header_file = """ 114 enum { 115 DISPLAY_SUCCESS = 1, 116 DISPLAY_FAILURE = 2, 117 }; 118 enum { 119 DISPLAY_START = 1, 120 DISPLAY_END = 2, 121 }; 122 """ 123 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 124 parser = HeaderParser() 125 parser._extract_enum(hjson["enums"]) 126 self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'LostName_0', 127 'members': [ 128 {'name': 'DISPLAY_SUCCESS', 'value': 1}, 129 {'name': 'DISPLAY_FAILURE', 'value': 2} 130 ]}, 131 {'name': 'LostName_1', 132 'members': [ 133 {'name': 'DISPLAY_START', 'value': 1}, 134 {'name': 'DISPLAY_END', 'value': 2} 135 ]}]) 136 137 def test_extract_union(self): 138 header_file = """ 139 union SceneDesc { 140 uint32_t id; 141 const char *desc; 142 }; 143 """ 144 parser = HeaderParser() 145 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 146 parser._extract_union(hjson["classes"]["SceneDesc"]) 147 self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': 'SceneDesc', 148 'type': 'union', 149 'members': [ 150 {'file_name': '/', 'line_number': 3, 151 'name': 'id', 'type': 'uint32_t'}, 152 {'file_name': '/', 'line_number': 4, 153 'name': 'desc', 'type': 'const char *'} 154 ]}]) 155 156 def test_extract_union_without_name(self): 157 header_file = """ 158 union { 159 uint32_t id; 160 const char *desc; 161 }; 162 union { 163 uint32_t id2; 164 const char *desc2; 165 }; 166 """ 167 parser = HeaderParser() 168 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 169 parser._extract_union(hjson["classes"]["<anon-union-1>"]) 170 parser._extract_union(hjson["classes"]["<anon-union-2>"]) 171 self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': '<anon-union-1>', 172 'type': 'union', 173 'members': [ 174 {'file_name': '/', 'line_number': 3, 175 'name': 'id', 'type': 'uint32_t'}, 176 {'file_name': '/', 'line_number': 4, 177 'name': 'desc', 'type': 'const char *'} 178 ]}, 179 {'name': '<anon-union-2>', 180 'type': 'union', 181 'members': [ 182 {'file_name': '/', 'line_number': 7, 183 'name': 'id2', 'type': 'uint32_t'}, 184 {'file_name': '/', 'line_number': 8, 185 'name': 'desc2', 'type': 'const char *'} 186 ]} 187 ]) 188 189 def test_extract_struct_include_union(self): 190 header_file = """ 191 struct AudioSceneDescriptor { 192 union SceneDesc { 193 uint32_t id; 194 const char *desc; 195 } scene; 196 }; 197 """ 198 parser = HeaderParser() 199 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 200 parser._extract_union(hjson["classes"]["AudioSceneDescriptor::SceneDesc"]) 201 self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': 'SceneDesc', 202 'type': 'union', 203 'members': [ 204 {'file_name': '/', 'line_number': 4, 205 'name': 'id', 'type': 'uint32_t'}, 206 {'file_name': '/', 'line_number': 5, 207 'name': 'desc', 'type': 'const char *'} 208 ]}]) 209 parser._extract_struct(hjson["classes"]["AudioSceneDescriptor"]) 210 self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'AudioSceneDescriptor', 211 'type': 'struct', 212 'members': [ 213 {'file_name': '/', 'line_number': 6, 214 'name': 'scene', 'type': 'SceneDesc'} 215 ]}]) 216 217 def test_extract_struct(self): 218 header_file = """ 219 typedef struct { 220 bool succeed; 221 int8_t end; 222 float rate; 223 double rate2; 224 cstring desc; 225 std::string location; 226 uint8_t status; 227 uint16_t busType; 228 int32_t num; 229 uint32_t count; 230 uint64_t timestamp; 231 void path; 232 char chipInfo[32]; 233 enum RetStatus status; 234 struct EvtPack package; 235 struct EvtPack *pkgPtr; 236 } DevAbility; 237 """ 238 parser = HeaderParser() 239 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 240 parser._extract_struct(hjson["classes"]["DevAbility"]) 241 self.assertSequenceEqual( 242 parser._header_dict.get("struct"), [{'name': 'DevAbility', 243 'type': 'struct', 244 'members': [ 245 {'file_name': '/', 'line_number': 3, 'name': 'succeed', 246 'type': 'bool'}, 247 {'file_name': '/', 'line_number': 4, 'name': 'end', 248 'type': 'int8_t'}, 249 {'file_name': '/', 'line_number': 5, 'name': 'rate', 250 'type': 'float'}, 251 {'file_name': '/', 'line_number': 6, 'name': 'rate2', 252 'type': 'double'}, 253 {'file_name': '/', 'line_number': 7, 'name': 'desc', 254 'type': 'cstring'}, 255 {'file_name': '/', 'line_number': 8, 'name': 'location', 256 'type': 'std::string'}, 257 {'file_name': '/', 'line_number': 9, 'name': 'status', 258 'type': 'uint8_t'}, 259 {'file_name': '/', 'line_number': 10, 'name': 'busType', 260 'type': 'uint16_t'}, 261 {'file_name': '/', 'line_number': 11, 'name': 'num', 262 'type': 'int32_t'}, 263 {'file_name': '/', 'line_number': 12, 'name': 'count', 264 'type': 'uint32_t'}, 265 {'file_name': '/', 'line_number': 13, 'name': 'timestamp', 266 'type': 'uint64_t'}, 267 {'file_name': '/', 'line_number': 14, 'name': 'path', 268 'type': 'void'}, 269 {'file_name': '/', 'line_number': 15, 'name': 'chipInfo', 270 'type': 'char *'}, 271 {'file_name': '/', 'line_number': 16, 'name': 'status', 272 'type': 'RetStatus'}, 273 {'file_name': '/', 'line_number': 17, 'name': 'package', 274 'type': 'struct EvtPack'}, 275 {'file_name': '/', 'line_number': 18, 'name': 'pkgPtr', 276 'type': 'struct EvtPack *'} 277 ]}]) 278 279 def test_extract_struct_with_enum_pointer(self): 280 header_file = """ 281 typedef struct { 282 enum Status *staPtr; 283 enum Test *testPtr; 284 } DevAbility; 285 """ 286 file_name = "header_file.h" 287 with open(file_name, 'w') as f: 288 f.write(header_file) 289 290 parser = HeaderParser() 291 back_file = parser._pre_handle(file_name) 292 hjson = json.loads(CppHeaderParser.CppHeader(file_name).toJSON()) 293 remove(back_file) 294 remove(file_name) 295 parser._extract_struct(hjson["classes"]["DevAbility"]) 296 self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'DevAbility', 297 'type': 'struct', 298 'members': [ 299 {'file_name': '/', 'line_number': 3, 300 'name': 'staPtr', 301 'type': 'Status_ENUM_POINTER'}, 302 {'file_name': '/', 'line_number': 4, 303 'name': 'testPtr', 304 'type': 'Test_ENUM_POINTER'} 305 ]}]) 306 307 def test_extract_struct_with_macro(self): 308 header_file = """ 309 #define CODE_SIZE 32 310 #define DIV_ROUND_UP(nr, d) (((nr) + (d) - 1) / (d)) 311 #define BYTE_HAS_BITS 8 312 #define BITS_TO_UINT64(count) DIV_ROUND_UP(count, BYTE_HAS_BITS * sizeof(unsigned long)) 313 314 typedef struct { 315 unsigned long absCode[BITS_TO_UINT64(CODE_SIZE)]; 316 } DevAbility; 317 """ 318 file_name = "header_file.h" 319 with open(file_name, 'w') as f: 320 f.write(header_file) 321 322 parser = HeaderParser() 323 back_file = parser._pre_handle(file_name) 324 hjson = json.loads(CppHeaderParser.CppHeader(file_name).toJSON()) 325 remove(back_file) 326 remove(file_name) 327 328 parser._extract_struct(hjson["classes"]["DevAbility"]) 329 self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'DevAbility', 330 'type': 'struct', 331 'members': [ 332 {'file_name': '/', 'line_number': 8, 333 'name': 'absCode', 'type': 'unsigned long *'} 334 ]}]) 335 336 def test_extract_struct_include_union_without_name(self): 337 header_file = """ 338 struct AudioSceneDescriptor { 339 union { 340 uint32_t id; 341 const char *desc; 342 }; 343 }; 344 """ 345 parser = HeaderParser() 346 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 347 parser._extract_union(hjson["classes"]["AudioSceneDescriptor::<anon-union-1>"]) 348 self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': '<anon-union-1>', 349 'type': 'union', 350 'members': [ 351 {'file_name': '/', 'line_number': 4, 352 'name': 'id', 'type': 'uint32_t'}, 353 {'file_name': '/', 'line_number': 5, 354 'name': 'desc', 'type': 'const char *'} 355 ]}]) 356 parser._extract_struct(hjson["classes"]["AudioSceneDescriptor"]) 357 self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'AudioSceneDescriptor', 358 'type': 'struct', 359 'members': [ 360 {'file_name': '/', 'line_number': 6, 361 'name': '', 'type': '<anon-union-1>'} 362 ]}]) 363 364 def test_extract_interface_with_method(self): 365 header_file = """ 366 typedef struct { 367 int32_t SetPowerStatus(uint32_t devIndex); 368 int32_t RunExtraCommand(uint32_t, InputExtraCmd *); 369 } InputController; 370 """ 371 parser = HeaderParser() 372 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 373 parser._extract_interface(hjson["classes"]["InputController"]) 374 self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'InputController', 375 'members': [ 376 {'name': 'SetPowerStatus', 377 'params': [{'name': 'devIndex', 378 'type': 'uint32_t'}], 379 'file_name': '/', 380 'line_number': 3}, 381 {'name': 'RunExtraCommand', 382 'params': [{'name': 'rand_name_0', 383 'type': 'uint32_t'}, 384 {'name': 'rand_name_1', 385 'type': 'InputExtraCmd *'} 386 ], 387 'file_name': '/', 388 'line_number': 4 389 }]}]) 390 391 def test_extract_interface_with_function_point(self): 392 header_file = """ 393 typedef struct { 394 int32_t (*SetPowerStatus)(uint32_t devIndex); 395 int32_t (*RunExtraCommand)(uint32_t, InputExtraCmd *); 396 int32_t (*RunCapacitanceTest)(uint32_t devIndex, 397 uint32_t testType); 398 } InputController; 399 """ 400 parser = HeaderParser() 401 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 402 parser._extract_interface(hjson["classes"]["InputController"]) 403 self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'InputController', 404 'members': [ 405 {'name': 'SetPowerStatus', 406 'params': [{'name': 'devIndex', 407 'type': 'uint32_t'}], 408 'file_name': '/', 409 'line_number': 3 410 }, 411 {'name': 'RunExtraCommand', 412 'params': [{'name': 'rand_name_0', 413 'type': 'uint32_t'}, 414 {'name': 'rand_name_1', 415 'type': 'InputExtraCmd *'} 416 ], 417 'file_name': '/', 418 'line_number': 4 419 }, 420 {'name': 'RunCapacitanceTest', 421 'params': [{'name': 'devIndex', 422 'type': 'uint32_t'}, 423 {'name': 'testType', 424 'type': 'uint32_t'} 425 ], 426 'file_name': '/', 427 'line_number': 5 428 } 429 ]}]) 430 431 def test_extract_interface_for_class(self): 432 header_file = """ 433 class ICameraDevice{ 434 public: 435 DECLARE_INTERFACE_DESCRIPTOR(u"HDI.Camera.V1_0.Device"); 436 virtual ~ICameraDevice() {} 437 virtual CamRetCode ICamera(); 438 virtual CamRetCode GetEnabledResults(std::vector<MetaType> &results) = 0; 439 virtual CamRetCode SetResultMode(const CallbackMode &mode) = 0; 440 }; 441 """ 442 parser = HeaderParser() 443 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 444 parser._extract_interface(hjson["classes"]["ICameraDevice"]) 445 self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'ICameraDevice', 446 'members': [ 447 {'name': 'ICamera', 448 'params': [], 449 'file_name': '/', 450 'line_number': 6 451 }, 452 {'name': 'GetEnabledResults', 453 'params': 454 [{'name': 'results', 455 'type': 'std::vector<MetaType> &'} 456 ], 457 'file_name': '/', 458 'line_number': 7 459 }, 460 {'name': 'SetResultMode', 461 'params': 462 [{'name': 'mode', 463 'type': 'const CallbackMode &'} 464 ], 465 'file_name': '/', 466 'line_number': 8 467 } 468 ]}]) 469 470 def test_extract_typedef(self): 471 header_file = """ 472 typedef void (*HotPlugCallback)(uint32_t devId, bool connected); 473 typedef void *AudioHandle; 474 """ 475 parser = HeaderParser() 476 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 477 parser._extract_typedef(hjson["typedefs"]) 478 self.assertSequenceEqual(parser._header_dict.get("typedef"), 479 [{'name': 'HotPlugCallback', 480 'type': '/* unsupported function pointer type: HotPlugCallback */'}, 481 {'name': 'AudioHandle', 482 'type': 'void *'}]) 483 484 def test_checkout_function_pointer_param(self): 485 header_file = """ 486 typedef struct { 487 int32_t (*Second)(bool vb, const int8_t vi8, const int16_t *vi16); 488 int32_t (*Minute)(struct Test st, const struct Test st); 489 int32_t (*Hour)(const struct Test *st, const struct Test * st); 490 int32_t (*Day)(const struct Test * st, const struct Test* st); 491 int32_t (*Month)(std::string str, unsigned int a, unsigned short, enum Test *c); 492 int32_t (*Year)(unsigned int **out1, const struct Test **out2); 493 } IFoo; 494 """ 495 parser = HeaderParser() 496 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 497 stack = hjson["classes"]["IFoo"] 498 second = stack["properties"]["public"][0] 499 params = parser._checkout_function_pointer_param(second["type"]) 500 self.assertSequenceEqual(params, [{'name': 'vb', 'type': 'bool'}, 501 {'name': 'vi8', 'type': 'int8_t'}, 502 {'name': 'vi16', 'type': 'int16_t *'}]) 503 504 minute = stack["properties"]["public"][1] 505 params = parser._checkout_function_pointer_param(minute["type"]) 506 self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test'}, {'name': 'st', 'type': 'Test'}]) 507 508 hour = stack["properties"]["public"][2] 509 params = parser._checkout_function_pointer_param(hour["type"]) 510 self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test *'}, {'name': 'st', 'type': 'Test *'}]) 511 512 day = stack["properties"]["public"][3] 513 params = parser._checkout_function_pointer_param(day["type"]) 514 self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test *'}, {'name': 'st', 'type': 'Test *'}]) 515 516 month = stack["properties"]["public"][4] 517 params = parser._checkout_function_pointer_param(month["type"]) 518 self.assertSequenceEqual(params, [{'name': 'str', 'type': 'std::string'}, 519 {'name': 'a', 'type': 'unsigned int'}, 520 {'name': 'rand_name_0', 'type': 'unsigned short'}, 521 {'name': 'c', 'type': 'Test *'}]) 522 523 year = stack["properties"]["public"][5] 524 params = parser._checkout_function_pointer_param(year["type"]) 525 self.assertSequenceEqual(params, [{'name': 'out1', 'type': 'unsigned int * *'}, 526 {'name': 'out2', 'type': 'Test * *'}]) 527 528 def test_checkout_function_pointer_param_with_struct(self): 529 header_file = """ 530 typedef struct { 531 int32_t (*GetPortCapability)(struct AudioAdapter *adapter, const struct AudioPort *port, 532 struct AudioPortCapability *capability); 533 } AudioAdapter; 534 """ 535 parser = HeaderParser() 536 hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) 537 stack = hjson["classes"]["AudioAdapter"] 538 function = stack["properties"]["public"][0] 539 params = parser._checkout_function_pointer_param(function["type"]) 540 self.assertSequenceEqual(params, [{'name': 'adapter', 'type': 'AudioAdapter *'}, 541 {'name': 'port', 'type': 'AudioPort *'}, 542 {'name': 'capability', 'type': 'AudioPortCapability *'}]) 543 544 545if __name__ == "__main__": 546 unittest.main() 547