• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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