# !/usr/bin/env python3 # coding=utf-8 """ * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. * * HDF is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * See the LICENSE file in the root of this repository for complete details. """ import json import sys import unittest from os import remove import CppHeaderParser sys.path.insert(0, '..') try: from _header_parser import HeaderParser finally: pass class HeaderParserTestCase(unittest.TestCase): def test_extract_path_and_file(self): parser = HeaderParser() parser._extract_path_and_file('include/test/audio_manager.h') self.assertEqual(parser._header_dict.get('path'), 'include/test') self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') parser._extract_path_and_file('./include/test/audio_manager.h') self.assertEqual(parser._header_dict.get('path'), './include/test') self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') parser._extract_path_and_file('../include/test/audio_manager.h') self.assertEqual(parser._header_dict.get('path'), '../include/test') self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') parser._extract_path_and_file('\\include\\test\\audio_manager.h') self.assertEqual(parser._header_dict.get('path'), '\\include\\test') self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') parser._extract_path_and_file('d:\\include\\test\\audio_manager.h') self.assertEqual(parser._header_dict.get('path'), 'd:\\include\\test') self.assertEqual(parser._header_dict.get('name'), 'audio_manager.h') def test_extract_include(self): parser = HeaderParser() parser._extract_include(['"buffer_handle.h"', '"buffer_type.h"']) self.assertSequenceEqual(parser._header_dict.get("import"), ['buffer_handle.h', 'buffer_type.h']) def test_extract_include_system_file(self): parser = HeaderParser() parser._extract_include(['', '', '"buffer_handle.h"', '"buffer_type.h"']) self.assertSequenceEqual(parser._header_dict.get("import"), ['buffer_handle.h', 'buffer_type.h']) def test_extract_enum(self): header_file = """ typedef enum { DISPLAY_SUCCESS = 0, DISPLAY_FAILURE = 1, } DispErrCode; """ hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser = HeaderParser() parser._extract_enum(hjson["enums"]) self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'DispErrCode', 'members': [{'name': 'DISPLAY_SUCCESS', 'value': 0}, {'name': 'DISPLAY_FAILURE', 'value': 1}]}]) def test_extract_enum_with_type(self): header_file = """ enum CamRetCode : int32_t { NO_ERROR = 0, CAMERA_BUSY = 1, }; """ hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser = HeaderParser() parser._extract_enum(hjson["enums"]) self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'CamRetCode', 'members': [{'name': 'NO_ERROR', 'value': 0}, {'name': 'CAMERA_BUSY', 'value': 1}]}]) def test_extract_enum_unexpected_value(self): header_file = """ typedef enum { DISPLAY_SUCCESS = 0x1234u, DISPLAY_FAILURE = -1, HBM_USE_CPU_READ = (1 << 0), } DispErrCode; """ hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) hjson["enums"][0]["filename"] = "test_header_parser.py" hjson["enums"][0]["line_number"] = 0 parser = HeaderParser() parser._extract_enum(hjson["enums"]) self.assertSequenceEqual( parser._header_dict.get("enum"), [{'name': 'DispErrCode', 'members': [ {'name': 'DISPLAY_SUCCESS', 'value': '0x1234 u // unexpected \'0x\''}, {'name': 'DISPLAY_FAILURE', 'value': '- 1 // unexpected \'-\''}, {'name': 'HBM_USE_CPU_READ', 'value': '( 1 << 0 ) // unexpected \'(\''} ]}]) def test_extract_enum_without_name(self): header_file = """ enum { DISPLAY_SUCCESS = 1, DISPLAY_FAILURE = 2, }; enum { DISPLAY_START = 1, DISPLAY_END = 2, }; """ hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser = HeaderParser() parser._extract_enum(hjson["enums"]) self.assertSequenceEqual(parser._header_dict.get("enum"), [{'name': 'LostName_0', 'members': [ {'name': 'DISPLAY_SUCCESS', 'value': 1}, {'name': 'DISPLAY_FAILURE', 'value': 2} ]}, {'name': 'LostName_1', 'members': [ {'name': 'DISPLAY_START', 'value': 1}, {'name': 'DISPLAY_END', 'value': 2} ]}]) def test_extract_union(self): header_file = """ union SceneDesc { uint32_t id; const char *desc; }; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_union(hjson["classes"]["SceneDesc"]) self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': 'SceneDesc', 'type': 'union', 'members': [ {'file_name': '/', 'line_number': 3, 'name': 'id', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 4, 'name': 'desc', 'type': 'const char *'} ]}]) def test_extract_union_without_name(self): header_file = """ union { uint32_t id; const char *desc; }; union { uint32_t id2; const char *desc2; }; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_union(hjson["classes"][""]) parser._extract_union(hjson["classes"][""]) self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': '', 'type': 'union', 'members': [ {'file_name': '/', 'line_number': 3, 'name': 'id', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 4, 'name': 'desc', 'type': 'const char *'} ]}, {'name': '', 'type': 'union', 'members': [ {'file_name': '/', 'line_number': 7, 'name': 'id2', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 8, 'name': 'desc2', 'type': 'const char *'} ]} ]) def test_extract_struct_include_union(self): header_file = """ struct AudioSceneDescriptor { union SceneDesc { uint32_t id; const char *desc; } scene; }; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_union(hjson["classes"]["AudioSceneDescriptor::SceneDesc"]) self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': 'SceneDesc', 'type': 'union', 'members': [ {'file_name': '/', 'line_number': 4, 'name': 'id', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 5, 'name': 'desc', 'type': 'const char *'} ]}]) parser._extract_struct(hjson["classes"]["AudioSceneDescriptor"]) self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'AudioSceneDescriptor', 'type': 'struct', 'members': [ {'file_name': '/', 'line_number': 6, 'name': 'scene', 'type': 'SceneDesc'} ]}]) def test_extract_struct(self): header_file = """ typedef struct { bool succeed; int8_t end; float rate; double rate2; cstring desc; std::string location; uint8_t status; uint16_t busType; int32_t num; uint32_t count; uint64_t timestamp; void path; char chipInfo[32]; enum RetStatus status; struct EvtPack package; struct EvtPack *pkgPtr; } DevAbility; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_struct(hjson["classes"]["DevAbility"]) self.assertSequenceEqual( parser._header_dict.get("struct"), [{'name': 'DevAbility', 'type': 'struct', 'members': [ {'file_name': '/', 'line_number': 3, 'name': 'succeed', 'type': 'bool'}, {'file_name': '/', 'line_number': 4, 'name': 'end', 'type': 'int8_t'}, {'file_name': '/', 'line_number': 5, 'name': 'rate', 'type': 'float'}, {'file_name': '/', 'line_number': 6, 'name': 'rate2', 'type': 'double'}, {'file_name': '/', 'line_number': 7, 'name': 'desc', 'type': 'cstring'}, {'file_name': '/', 'line_number': 8, 'name': 'location', 'type': 'std::string'}, {'file_name': '/', 'line_number': 9, 'name': 'status', 'type': 'uint8_t'}, {'file_name': '/', 'line_number': 10, 'name': 'busType', 'type': 'uint16_t'}, {'file_name': '/', 'line_number': 11, 'name': 'num', 'type': 'int32_t'}, {'file_name': '/', 'line_number': 12, 'name': 'count', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 13, 'name': 'timestamp', 'type': 'uint64_t'}, {'file_name': '/', 'line_number': 14, 'name': 'path', 'type': 'void'}, {'file_name': '/', 'line_number': 15, 'name': 'chipInfo', 'type': 'char *'}, {'file_name': '/', 'line_number': 16, 'name': 'status', 'type': 'RetStatus'}, {'file_name': '/', 'line_number': 17, 'name': 'package', 'type': 'struct EvtPack'}, {'file_name': '/', 'line_number': 18, 'name': 'pkgPtr', 'type': 'struct EvtPack *'} ]}]) def test_extract_struct_with_enum_pointer(self): header_file = """ typedef struct { enum Status *staPtr; enum Test *testPtr; } DevAbility; """ file_name = "header_file.h" with open(file_name, 'w') as f: f.write(header_file) parser = HeaderParser() back_file = parser._pre_handle(file_name) hjson = json.loads(CppHeaderParser.CppHeader(file_name).toJSON()) remove(back_file) remove(file_name) parser._extract_struct(hjson["classes"]["DevAbility"]) self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'DevAbility', 'type': 'struct', 'members': [ {'file_name': '/', 'line_number': 3, 'name': 'staPtr', 'type': 'Status_ENUM_POINTER'}, {'file_name': '/', 'line_number': 4, 'name': 'testPtr', 'type': 'Test_ENUM_POINTER'} ]}]) def test_extract_struct_with_macro(self): header_file = """ #define CODE_SIZE 32 #define DIV_ROUND_UP(nr, d) (((nr) + (d) - 1) / (d)) #define BYTE_HAS_BITS 8 #define BITS_TO_UINT64(count) DIV_ROUND_UP(count, BYTE_HAS_BITS * sizeof(unsigned long)) typedef struct { unsigned long absCode[BITS_TO_UINT64(CODE_SIZE)]; } DevAbility; """ file_name = "header_file.h" with open(file_name, 'w') as f: f.write(header_file) parser = HeaderParser() back_file = parser._pre_handle(file_name) hjson = json.loads(CppHeaderParser.CppHeader(file_name).toJSON()) remove(back_file) remove(file_name) parser._extract_struct(hjson["classes"]["DevAbility"]) self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'DevAbility', 'type': 'struct', 'members': [ {'file_name': '/', 'line_number': 8, 'name': 'absCode', 'type': 'unsigned long *'} ]}]) def test_extract_struct_include_union_without_name(self): header_file = """ struct AudioSceneDescriptor { union { uint32_t id; const char *desc; }; }; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_union(hjson["classes"]["AudioSceneDescriptor::"]) self.assertSequenceEqual(parser._header_dict.get("union"), [{'name': '', 'type': 'union', 'members': [ {'file_name': '/', 'line_number': 4, 'name': 'id', 'type': 'uint32_t'}, {'file_name': '/', 'line_number': 5, 'name': 'desc', 'type': 'const char *'} ]}]) parser._extract_struct(hjson["classes"]["AudioSceneDescriptor"]) self.assertSequenceEqual(parser._header_dict.get("struct"), [{'name': 'AudioSceneDescriptor', 'type': 'struct', 'members': [ {'file_name': '/', 'line_number': 6, 'name': '', 'type': ''} ]}]) def test_extract_interface_with_method(self): header_file = """ typedef struct { int32_t SetPowerStatus(uint32_t devIndex); int32_t RunExtraCommand(uint32_t, InputExtraCmd *); } InputController; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_interface(hjson["classes"]["InputController"]) self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'InputController', 'members': [ {'name': 'SetPowerStatus', 'params': [{'name': 'devIndex', 'type': 'uint32_t'}], 'file_name': '/', 'line_number': 3}, {'name': 'RunExtraCommand', 'params': [{'name': 'rand_name_0', 'type': 'uint32_t'}, {'name': 'rand_name_1', 'type': 'InputExtraCmd *'} ], 'file_name': '/', 'line_number': 4 }]}]) def test_extract_interface_with_function_point(self): header_file = """ typedef struct { int32_t (*SetPowerStatus)(uint32_t devIndex); int32_t (*RunExtraCommand)(uint32_t, InputExtraCmd *); int32_t (*RunCapacitanceTest)(uint32_t devIndex, uint32_t testType); } InputController; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_interface(hjson["classes"]["InputController"]) self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'InputController', 'members': [ {'name': 'SetPowerStatus', 'params': [{'name': 'devIndex', 'type': 'uint32_t'}], 'file_name': '/', 'line_number': 3 }, {'name': 'RunExtraCommand', 'params': [{'name': 'rand_name_0', 'type': 'uint32_t'}, {'name': 'rand_name_1', 'type': 'InputExtraCmd *'} ], 'file_name': '/', 'line_number': 4 }, {'name': 'RunCapacitanceTest', 'params': [{'name': 'devIndex', 'type': 'uint32_t'}, {'name': 'testType', 'type': 'uint32_t'} ], 'file_name': '/', 'line_number': 5 } ]}]) def test_extract_interface_for_class(self): header_file = """ class ICameraDevice{ public: DECLARE_INTERFACE_DESCRIPTOR(u"HDI.Camera.V1_0.Device"); virtual ~ICameraDevice() {} virtual CamRetCode ICamera(); virtual CamRetCode GetEnabledResults(std::vector &results) = 0; virtual CamRetCode SetResultMode(const CallbackMode &mode) = 0; }; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_interface(hjson["classes"]["ICameraDevice"]) self.assertSequenceEqual(parser._header_dict.get("interface"), [{'name': 'ICameraDevice', 'members': [ {'name': 'ICamera', 'params': [], 'file_name': '/', 'line_number': 6 }, {'name': 'GetEnabledResults', 'params': [{'name': 'results', 'type': 'std::vector &'} ], 'file_name': '/', 'line_number': 7 }, {'name': 'SetResultMode', 'params': [{'name': 'mode', 'type': 'const CallbackMode &'} ], 'file_name': '/', 'line_number': 8 } ]}]) def test_extract_typedef(self): header_file = """ typedef void (*HotPlugCallback)(uint32_t devId, bool connected); typedef void *AudioHandle; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) parser._extract_typedef(hjson["typedefs"]) self.assertSequenceEqual(parser._header_dict.get("typedef"), [{'name': 'HotPlugCallback', 'type': '/* unsupported function pointer type: HotPlugCallback */'}, {'name': 'AudioHandle', 'type': 'void *'}]) def test_checkout_function_pointer_param(self): header_file = """ typedef struct { int32_t (*Second)(bool vb, const int8_t vi8, const int16_t *vi16); int32_t (*Minute)(struct Test st, const struct Test st); int32_t (*Hour)(const struct Test *st, const struct Test * st); int32_t (*Day)(const struct Test * st, const struct Test* st); int32_t (*Month)(std::string str, unsigned int a, unsigned short, enum Test *c); int32_t (*Year)(unsigned int **out1, const struct Test **out2); } IFoo; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) stack = hjson["classes"]["IFoo"] second = stack["properties"]["public"][0] params = parser._checkout_function_pointer_param(second["type"]) self.assertSequenceEqual(params, [{'name': 'vb', 'type': 'bool'}, {'name': 'vi8', 'type': 'int8_t'}, {'name': 'vi16', 'type': 'int16_t *'}]) minute = stack["properties"]["public"][1] params = parser._checkout_function_pointer_param(minute["type"]) self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test'}, {'name': 'st', 'type': 'Test'}]) hour = stack["properties"]["public"][2] params = parser._checkout_function_pointer_param(hour["type"]) self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test *'}, {'name': 'st', 'type': 'Test *'}]) day = stack["properties"]["public"][3] params = parser._checkout_function_pointer_param(day["type"]) self.assertSequenceEqual(params, [{'name': 'st', 'type': 'Test *'}, {'name': 'st', 'type': 'Test *'}]) month = stack["properties"]["public"][4] params = parser._checkout_function_pointer_param(month["type"]) self.assertSequenceEqual(params, [{'name': 'str', 'type': 'std::string'}, {'name': 'a', 'type': 'unsigned int'}, {'name': 'rand_name_0', 'type': 'unsigned short'}, {'name': 'c', 'type': 'Test *'}]) year = stack["properties"]["public"][5] params = parser._checkout_function_pointer_param(year["type"]) self.assertSequenceEqual(params, [{'name': 'out1', 'type': 'unsigned int * *'}, {'name': 'out2', 'type': 'Test * *'}]) def test_checkout_function_pointer_param_with_struct(self): header_file = """ typedef struct { int32_t (*GetPortCapability)(struct AudioAdapter *adapter, const struct AudioPort *port, struct AudioPortCapability *capability); } AudioAdapter; """ parser = HeaderParser() hjson = json.loads(CppHeaderParser.CppHeader(header_file, "string").toJSON()) stack = hjson["classes"]["AudioAdapter"] function = stack["properties"]["public"][0] params = parser._checkout_function_pointer_param(function["type"]) self.assertSequenceEqual(params, [{'name': 'adapter', 'type': 'AudioAdapter *'}, {'name': 'port', 'type': 'AudioPort *'}, {'name': 'capability', 'type': 'AudioPortCapability *'}]) if __name__ == "__main__": unittest.main()