• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Tests for code."""
15
16from __future__ import absolute_import
17from __future__ import division
18from __future__ import print_function
19from absl.testing import absltest
20from absl.testing import parameterized
21from clang import cindex
22from com_google_sandboxed_api.sandboxed_api.tools.python_generator import code
23from com_google_sandboxed_api.sandboxed_api.tools.python_generator import code_test_util
24
25CODE = """
26typedef int(fun*)(int,int);
27extern "C" int function_a(int x, int y) { return x + y; }
28extern "C" int function_b(int a, int b) { return a + b; }
29
30struct a {
31  void (*fun_ptr)(char, long);
32}
33"""
34
35
36def analyze_string(
37    content, path='tmp.cc', limit_scan_depth=False, func_names=None
38):
39  """Returns Analysis object for in memory content."""
40  return analyze_strings(path, [(path, content)], limit_scan_depth, func_names)
41
42
43def analyze_strings(
44    path, unsaved_files, limit_scan_depth=False, func_names=None
45):
46  """Returns Analysis object for in memory content."""
47  return code.Analyzer._analyze_file_for_tu(
48      path, None, False, unsaved_files, limit_scan_depth, func_names
49  )
50
51
52class CodeAnalysisTest(parameterized.TestCase):
53
54  def testInMemoryFile(self):
55    translation_unit = analyze_string(CODE)
56    self.assertIsNotNone(translation_unit._tu.cursor)
57
58  def testSimpleASTTraversal(self):
59    translation_unit = analyze_string(CODE)
60
61    structs = 0
62    functions = 0
63    params = 0
64    typedefs = 0
65
66    for cursor in translation_unit._walk_preorder():
67      if cursor.kind == cindex.CursorKind.FUNCTION_DECL:
68        functions += 1
69      elif cursor.kind == cindex.CursorKind.STRUCT_DECL:
70        structs += 1
71      elif cursor.kind == cindex.CursorKind.PARM_DECL:
72        params += 1
73      elif cursor.kind == cindex.CursorKind.TYPEDEF_DECL:
74        typedefs += 1
75
76    self.assertEqual(functions, 2)
77    self.assertEqual(structs, 1)
78    self.assertEqual(params, 8)
79    self.assertEqual(typedefs, 1)
80
81  def testParseSkipFunctionBodies(self):
82    function_body = 'extern "C"  int function(bool a1) { return a1 ? 1 : 2; }'
83    translation_unit = analyze_string(function_body)
84    for cursor in translation_unit._walk_preorder():
85      if cursor.kind == cindex.CursorKind.FUNCTION_DECL:
86        # cursor.get_definition() is None when we skip parsing function bodies
87        self.assertIsNone(cursor.get_definition())
88
89  def testExternC(self):
90    translation_unit = analyze_string('extern "C" int function(char* a);')
91    cursor_kinds = [
92        x.kind
93        for x in translation_unit._walk_preorder()
94        if x.kind != cindex.CursorKind.MACRO_DEFINITION
95    ]
96    for kind in [
97        cindex.CursorKind.TRANSLATION_UNIT,
98        cindex.CursorKind.FUNCTION_DECL,
99        cindex.CursorKind.PARM_DECL,
100    ]:
101      self.assertIn(kind, cursor_kinds)
102    self.assertTrue(
103        cindex.CursorKind.LINKAGE_SPEC in cursor_kinds
104        # Older libclang versions to not have a cursor type for linkage specs
105        or cindex.CursorKind.UNEXPOSED_DECL in cursor_kinds
106    )
107
108  @parameterized.named_parameters(
109      ('1:', '/tmp/test.h', 'tmp', 'tmp/test.h'),
110      ('2:', '/a/b/c/d/tmp/test.h', 'c/d', 'c/d/tmp/test.h'),
111      ('3:', '/tmp/test.h', None, '/tmp/test.h'),
112      ('4:', '/tmp/test.h', '', '/tmp/test.h'),
113      ('5:', '/tmp/test.h', 'xxx', 'xxx/test.h'),
114  )
115  def testGetIncludes(self, path, prefix, expected):
116    function_body = 'extern "C" int function(bool a1) { return a1 ? 1 : 2; }'
117    translation_unit = analyze_string(function_body)
118    for cursor in translation_unit._walk_preorder():
119      if cursor.kind == cindex.CursorKind.FUNCTION_DECL:
120        fn = code.Function(translation_unit, cursor)
121        fn.get_absolute_path = lambda: path
122        self.assertEqual(fn.get_include_path(prefix), expected)
123
124  def testCodeGeneratorOutput(self):
125    body = """
126      extern "C" {
127        int function_a(int x, int y) { return x + y; }
128
129        int types_1(bool a0, unsigned char a1, char a2, unsigned short a3, short a4);
130        int types_2(int a0, unsigned int a1, long a2, unsigned long a3);
131        int types_3(long long a0, unsigned long long a1, float a2, double a3);
132        int types_4(signed char a0, signed short a1, signed int a2, signed long a3);
133        int types_5(signed long long a0, long double a1);
134        void types_6(char* a0);
135      }
136    """
137    functions = [
138        'function_a',
139        'types_1',
140        'types_2',
141        'types_3',
142        'types_4',
143        'types_5',
144        'types_6',
145    ]
146    generator = code.Generator([analyze_string(body, func_names=functions)])
147    result = generator.generate('Test', 'sapi::Tests', None, None)
148    self.assertMultiLineEqual(code_test_util.CODE_GOLD, result)
149
150  def testElaboratedArgument(self):
151    body = """
152      struct x { int a; };
153      extern "C" int function(struct x a) { return a.a; }
154    """
155    generator = code.Generator([analyze_string(body, func_names=['function'])])
156    with self.assertRaisesRegex(ValueError, r'Elaborate.*mapped.*'):
157      generator.generate('Test', 'sapi::Tests', None, None)
158
159  def testElaboratedArgument2(self):
160    body = """
161      typedef struct { int a; char b; } x;
162      extern "C" int function(x a) { return a.a; }
163    """
164    generator = code.Generator([analyze_string(body, func_names=['function'])])
165    with self.assertRaisesRegex(ValueError, r'Elaborate.*mapped.*'):
166      generator.generate('Test', 'sapi::Tests', None, None)
167
168  def testGetMappedType(self):
169    body = """
170      typedef unsigned int uint;
171      typedef uint* uintp;
172      extern "C" uint function(uintp a) { return *a; }
173    """
174    generator = code.Generator([analyze_string(body)])
175    result = generator.generate('Test', 'sapi::Tests', None, None)
176    self.assertMultiLineEqual(code_test_util.CODE_GOLD_MAPPED, result)
177
178  @parameterized.named_parameters(
179      ('1:', '/tmp/test.h', '_TMP_TEST_H_'),
180      ('2:', 'tmp/te-st.h', 'TMP_TE_ST_H_'),
181      ('3:', 'tmp/te-st.h.gen', 'TMP_TE_ST_H_'),
182      ('4:', 'xx/genfiles/tmp/te-st.h', 'TMP_TE_ST_H_'),
183      ('5:', 'xx/genfiles/tmp/te-st.h.gen', 'TMP_TE_ST_H_'),
184      ('6:', 'xx/genfiles/.gen/tmp/te-st.h', '_GEN_TMP_TE_ST_H_'),
185  )
186  def testGetHeaderGuard(self, path, expected):
187    self.assertEqual(code.get_header_guard(path), expected)
188
189  @parameterized.named_parameters(
190      (
191          'function with return value and arguments',
192          'extern "C" int function(bool arg_bool, char* arg_ptr);',
193          ['arg_bool', 'arg_ptr'],
194      ),
195      (
196          'function without return value and no arguments',
197          'extern "C" void function();',
198          [],
199      ),
200  )
201  def testArgumentNames(self, body, names):
202    generator = code.Generator([analyze_string(body)])
203    functions = generator._get_functions()
204    self.assertLen(functions, 1)
205    self.assertLen(functions[0].argument_types, len(names))
206    # Extra check for generation, in case rendering throws error for this test.
207    generator.generate('Test', 'sapi::Tests', None, None)
208    for t in functions[0].argument_types:
209      self.assertIn(t.name, names)
210
211  def testStaticFunctions(self):
212    body = 'static int function() { return 7; };'
213    generator = code.Generator([analyze_string(body)])
214    self.assertEmpty(generator._get_functions())
215
216  def testEnumGeneration(self):
217    body = """
218      enum ProcessStatus {
219        OK = 0,
220        ERROR = 1,
221      };
222
223      extern "C" ProcessStatus ProcessDatapoint(ProcessStatus status) {
224        return status;
225      }
226    """
227    generator = code.Generator([analyze_string(body)])
228    result = generator.generate('Test', 'sapi::Tests', None, None)
229    self.assertMultiLineEqual(code_test_util.CODE_ENUM_GOLD, result)
230
231  def testTypeEq(self):
232    body = """
233    typedef unsigned int uint;
234    extern "C" void function(uint a1, uint a2, char a3);
235    """
236    generator = code.Generator([analyze_string(body)])
237    functions = generator._get_functions()
238    self.assertLen(functions, 1)
239
240    args = functions[0].arguments()
241    self.assertLen(args, 3)
242    self.assertEqual(args[0], args[1])
243    self.assertNotEqual(args[0], args[2])
244    self.assertNotEqual(args[1], args[2])
245
246    self.assertLen(set(args), 2)
247    # Extra check for generation, in case rendering throws error for this test.
248    generator.generate('Test', 'sapi::Tests', None, None)
249
250  def testTypedefRelatedTypes(self):
251    body = """
252      typedef unsigned int uint;
253      typedef uint* uint_p;
254      typedef uint_p* uint_pp;
255
256      typedef struct data {
257        int a;
258        int b;
259      } data_s;
260      typedef data_s* data_p;
261
262      extern "C" uint function_using_typedefs(uint_p a1, uint_pp a2, data_p a3);
263    """
264    generator = code.Generator([analyze_string(body)])
265    functions = generator._get_functions()
266    self.assertLen(functions, 1)
267
268    args = functions[0].arguments()
269    self.assertLen(args, 3)
270
271    types = args[0].get_related_types()
272    names = [t._clang_type.spelling for t in types]
273    self.assertLen(types, 2)
274    self.assertSameElements(names, ['uint_p', 'uint'])
275
276    types = args[1].get_related_types()
277    names = [t._clang_type.spelling for t in types]
278    self.assertLen(types, 3)
279    self.assertSameElements(names, ['uint_pp', 'uint_p', 'uint'])
280
281    types = args[2].get_related_types()
282    names = [t._clang_type.spelling for t in types]
283    self.assertLen(types, 2)
284    self.assertSameElements(names, ['data_s', 'data_p'])
285
286    # Extra check for generation, in case rendering throws error for this test.
287    generator.generate('Test', 'sapi::Tests', None, None)
288
289  def testTypedefDuplicateType(self):
290    body = """
291      typedef struct data {
292        int a;
293        int b;
294      } data_s;
295
296      struct s {
297        struct data* f1;
298      };
299
300      extern "C" uint function_using_typedefs(struct s* a1, data_s* a2);
301    """
302    generator = code.Generator([analyze_string(body)])
303    functions = generator._get_functions()
304    self.assertLen(functions, 1)
305
306    args = functions[0].arguments()
307    self.assertLen(args, 2)
308
309    types = generator._get_related_types()
310    self.assertLen(generator.translation_units[0].types_to_skip, 1)
311
312    names = [t._clang_type.spelling for t in types]
313    self.assertSameElements(['data_s', 's'], names)
314
315    # Extra check for generation, in case rendering throws error for this test.
316    generator.generate('Test', 'sapi::Tests', None, None)
317
318  def testStructureRelatedTypes(self):
319    body = """
320      typedef unsigned int uint;
321
322      typedef struct {
323        uint a;
324        struct {
325          int a;
326          int b;
327        } b;
328      } struct_1;
329
330      struct struct_2 {
331        uint a;
332        char b;
333        struct_1* c;
334      };
335
336      typedef struct a {
337        int b;
338      } struct_a;
339
340      extern "C" int function_using_structures(struct struct_2* a1, struct_1* a2,
341      struct_a* a3);
342    """
343    generator = code.Generator([analyze_string(body)])
344    functions = generator._get_functions()
345    self.assertLen(functions, 1)
346
347    args = functions[0].arguments()
348    self.assertLen(args, 3)
349
350    types = args[0].get_related_types()
351    names = [t._clang_type.spelling for t in types]
352    self.assertLen(types, 3)
353    self.assertSameElements(names, ['struct_2', 'uint', 'struct_1'])
354
355    types = args[1].get_related_types()
356    names = [t._clang_type.spelling for t in types]
357    self.assertLen(types, 2)
358    self.assertSameElements(names, ['struct_1', 'uint'])
359
360    names = [t._clang_type.spelling for t in generator._get_related_types()]
361    self.assertEqual(names, ['uint', 'struct_1', 'struct_2', 'struct_a'])
362
363    types = args[2].get_related_types()
364    self.assertLen(types, 1)
365
366    # Extra check for generation, in case rendering throws error for this test.
367    generator.generate('Test', 'sapi::Tests', None, None)
368
369  def testUnionRelatedTypes(self):
370    body = """
371      typedef unsigned int uint;
372
373      typedef union {
374        uint a;
375        union {
376          int a;
377          int b;
378        } b;
379      } union_1;
380
381      union union_2 {
382        uint a;
383        char b;
384        union_1* c;
385      };
386
387      extern "C" int function_using_unions(union union_2* a1, union_1* a2);
388    """
389    generator = code.Generator([analyze_string(body)])
390    functions = generator._get_functions()
391    self.assertLen(functions, 1)
392
393    args = functions[0].arguments()
394    self.assertLen(args, 2)
395
396    types = args[0].get_related_types()
397    names = [t._clang_type.spelling for t in types]
398    self.assertLen(types, 3)
399    self.assertSameElements(names, ['union_2', 'uint', 'union_1'])
400
401    types = args[1].get_related_types()
402    names = [t._clang_type.spelling for t in types]
403    self.assertLen(types, 2)
404    self.assertSameElements(names, ['union_1', 'uint'])
405
406    # Extra check for generation, in case rendering throws error for this test.
407    generator.generate('Test', 'sapi::Tests', None, None)
408
409  def testFunctionPointerRelatedTypes(self):
410    body = """
411      typedef unsigned int uint;
412      typedef unsigned char uchar;
413      typedef uint (*funcp)(uchar, uchar);
414
415      struct struct_1 {
416        uint (*func)(uchar);
417        int a;
418      };
419
420      extern "C" void function(struct struct_1* a1, funcp a2);
421    """
422    generator = code.Generator([analyze_string(body)])
423    functions = generator._get_functions()
424    self.assertLen(functions, 1)
425
426    args = functions[0].arguments()
427    self.assertLen(args, 2)
428
429    types = args[0].get_related_types()
430    names = [t._clang_type.spelling for t in types]
431    self.assertLen(types, 3)
432    self.assertSameElements(names, ['struct_1', 'uint', 'uchar'])
433
434    types = args[1].get_related_types()
435    names = [t._clang_type.spelling for t in types]
436    self.assertLen(types, 3)
437    self.assertSameElements(names, ['funcp', 'uint', 'uchar'])
438
439    # Extra check for generation, in case rendering throws error for this test.
440    generator.generate('Test', 'sapi::Tests', None, None)
441
442  def testForwardDeclaration(self):
443    body = """
444      struct struct_6_def;
445      typedef struct struct_6_def struct_6;
446      typedef struct_6* struct_6p;
447      typedef void (*function_p3)(struct_6p);
448      struct struct_6_def {
449        function_p3 fn;
450      };
451
452      extern "C" void function_using_type_loop(struct_6p a1);
453    """
454    generator = code.Generator([analyze_string(body)])
455    functions = generator._get_functions()
456    self.assertLen(functions, 1)
457
458    args = functions[0].arguments()
459    self.assertLen(args, 1)
460
461    types = args[0].get_related_types()
462    names = [t._clang_type.spelling for t in types]
463    self.assertLen(types, 4)
464    self.assertSameElements(
465        names, ['struct_6p', 'struct_6', 'struct_6_def', 'function_p3']
466    )
467
468    self.assertLen(generator.translation_units, 1)
469    self.assertLen(generator.translation_units[0].forward_decls, 1)
470
471    t = next(x for x in types if x._clang_type.spelling == 'struct_6_def')
472    self.assertIn(t, generator.translation_units[0].forward_decls)
473
474    names = [t._clang_type.spelling for t in generator._get_related_types()]
475    self.assertEqual(
476        names, ['struct_6', 'struct_6p', 'function_p3', 'struct_6_def']
477    )
478
479    # Extra check for generation, in case rendering throws error for this test.
480    forward_decls = generator._get_forward_decls(generator._get_related_types())
481    self.assertLen(forward_decls, 1)
482    self.assertEqual(forward_decls[0], 'struct struct_6_def;')
483    generator.generate('Test', 'sapi::Tests', None, None)
484
485  def testEnumRelatedTypes(self):
486    body = """
487      enum Enumeration { ONE, TWO, THREE };
488      typedef enum Numbers { UNKNOWN, FIVE = 5, SE7EN = 7 } Nums;
489      typedef enum { SIX = 6, TEN = 10 } SixOrTen;
490      enum class Color : long long { RED, GREEN = 20, BLUE };  // NOLINT
491      enum struct Direction { LEFT = 'l', RIGHT = 'r' };
492      enum __rlimit_resource {  RLIMIT_CPU = 0, RLIMIT_MEM = 1};
493
494      extern "C" int function_using_enums(Enumeration a1, SixOrTen a2, Color a3,
495                           Direction a4, Nums a5, enum __rlimit_resource a6);
496     """
497    generator = code.Generator([analyze_string(body)])
498    functions = generator._get_functions()
499    self.assertLen(functions, 1)
500
501    args = functions[0].arguments()
502    self.assertLen(args, 6)
503
504    self.assertLen(args[0].get_related_types(), 1)
505    self.assertLen(args[1].get_related_types(), 1)
506    self.assertLen(args[2].get_related_types(), 1)
507    self.assertLen(args[3].get_related_types(), 1)
508    self.assertLen(args[4].get_related_types(), 1)
509    self.assertLen(args[5].get_related_types(), 1)
510
511    # Extra check for generation, in case rendering throws error for this test.
512    generator.generate('Test', 'sapi::Tests', None, None)
513
514  def testArrayAsParam(self):
515    body = """
516      extern "C" int function_using_enums(char a[10], char *const __argv[]);
517     """
518    generator = code.Generator([analyze_string(body)])
519    functions = generator._get_functions()
520    self.assertLen(functions, 1)
521
522    args = functions[0].arguments()
523    self.assertLen(args, 2)
524
525  @parameterized.named_parameters(
526      ('uint < ushort  ', 'assertLess', 1, 2),
527      ('uint < chr     ', 'assertLess', 1, 3),
528      ('uint < uchar   ', 'assertLess', 1, 4),
529      ('uint < u32     ', 'assertLess', 1, 5),
530      ('uint < ulong   ', 'assertLess', 1, 6),
531      ('ushort < chr   ', 'assertLess', 2, 3),
532      ('ushort < uchar ', 'assertLess', 2, 4),
533      ('ushort < u32   ', 'assertLess', 2, 5),
534      ('ushort < ulong ', 'assertLess', 2, 6),
535      ('chr < uchar    ', 'assertLess', 3, 4),
536      ('chr < u32      ', 'assertLess', 3, 5),
537      ('chr < ulong    ', 'assertLess', 3, 6),
538      ('uchar < u32    ', 'assertLess', 4, 5),
539      ('uchar < ulong  ', 'assertLess', 4, 6),
540      ('u32 < ulong    ', 'assertLess', 5, 6),
541      ('ushort > uint  ', 'assertGreater', 2, 1),
542      ('chr > uint     ', 'assertGreater', 3, 1),
543      ('uchar > uint   ', 'assertGreater', 4, 1),
544      ('u32 > uint     ', 'assertGreater', 5, 1),
545      ('ulong > uint   ', 'assertGreater', 6, 1),
546      ('chr > ushort   ', 'assertGreater', 3, 2),
547      ('uchar > ushort ', 'assertGreater', 4, 2),
548      ('u32 > ushort   ', 'assertGreater', 5, 2),
549      ('ulong > ushort ', 'assertGreater', 6, 2),
550      ('uchar > chr    ', 'assertGreater', 4, 3),
551      ('u32 > chr      ', 'assertGreater', 5, 3),
552      ('ulong > chr    ', 'assertGreater', 6, 3),
553      ('u32 > uchar    ', 'assertGreater', 5, 4),
554      ('ulong > uchar  ', 'assertGreater', 6, 4),
555      ('ulong > u32    ', 'assertGreater', 6, 5),
556  )
557  def testTypeOrder(self, func, a1, a2):
558    """Checks if comparison functions of Type class work properly.
559
560    This is necessary for Generator._get_related_types to return types in
561    proper order, ready to be emitted in the generated file. To be more
562    specific: emitted types will be ordered in a way that would allow
563    compilation ie. if structure field type is a typedef, typedef definition
564    will end up before structure definition.
565
566    Args:
567      func: comparison assert to call
568      a1: function argument number to take the type to compare
569      a2: function argument number to take the type to compare
570    """
571
572    file1_code = """
573    typedef unsigned int uint;
574    #include "/f2.h"
575    typedef uint u32;
576    #include "/f3.h"
577
578    struct args {
579      u32 a;
580      uchar b;
581      ulong c;
582      ushort d;
583      chr e;
584    };
585    extern "C" int function(struct args* a0, uint a1, ushort a2, chr a3,
586                 uchar a4, u32 a5, ulong a6, struct args* a7);
587    """
588    file2_code = """
589    typedef unsigned short ushort;
590    #include "/f4.h"
591    typedef unsigned char uchar;"""
592    file3_code = 'typedef unsigned long ulong;'
593    file4_code = 'typedef char chr;'
594    files = [
595        ('f1.h', file1_code),
596        ('/f2.h', file2_code),
597        ('/f3.h', file3_code),
598        ('/f4.h', file4_code),
599    ]
600    generator = code.Generator([analyze_strings('f1.h', files)])
601    functions = generator._get_functions()
602    self.assertLen(functions, 1)
603
604    args = functions[0].arguments()
605    getattr(self, func)(args[a1], args[a2])
606    # Extra check for generation, in case rendering throws error for this test.
607    generator.generate('Test', 'sapi::Tests', None, None)
608
609  def testFilterFunctionsFromInputFilesOnly(self):
610    file1_code = """
611      #include "/f2.h"
612
613      extern "C" int function1();
614    """
615    file2_code = """
616      extern "C" int function2();
617    """
618
619    files = [('f1.h', file1_code), ('/f2.h', file2_code)]
620    generator = code.Generator([analyze_strings('f1.h', files)])
621    functions = generator._get_functions()
622    self.assertLen(functions, 2)
623
624    generator = code.Generator([analyze_strings('f1.h', files, True)])
625    functions = generator._get_functions()
626    self.assertLen(functions, 1)
627
628  def testTypeToString(self):
629    body = """
630      #define SIZE 1024
631      typedef unsigned int uint;
632
633      typedef struct {
634      #if SOME_DEFINE >= 12 \
635      && SOME_OTHER == 13
636        uint a;
637      #else
638        uint aa;
639      #endif
640        struct {
641          uint a;
642          int b;
643          char c[SIZE];
644        } b;
645      } struct_1;
646
647      extern "C" int function_using_structures(struct_1* a1);
648    """
649
650    # pylint: disable=trailing-whitespace
651    expected = """typedef struct {
652#if SOME_DEFINE >= 12 && SOME_OTHER == 13
653\tuint a ;
654#else
655\tuint aa ;
656#endif
657\tstruct {
658\t\tuint a ;
659\t\tint b ;
660\t\tchar c [ SIZE ] ;
661\t} b ;
662} struct_1"""
663    generator = code.Generator([analyze_string(body)])
664    functions = generator._get_functions()
665    self.assertLen(functions, 1)
666
667    types = generator._get_related_types()
668    self.assertLen(types, 2)
669    self.assertEqual('typedef unsigned int uint', types[0].stringify())
670    self.assertMultiLineEqual(expected, types[1].stringify())
671
672    # Extra check for generation, in case rendering throws error for this test.
673    generator.generate('Test', 'sapi::Tests', None, None)
674
675  def testCollectDefines(self):
676    body = """
677      #define SIZE 1024
678      #define NOT_USED 7
679      #define SIZE2 2*1024
680      #define SIZE3 1337
681      #define SIZE4 10
682      struct test {
683        int a[SIZE];
684        char b[SIZE2];
685        float c[777];
686        int (*d)[SIZE3*SIZE4];
687      };
688      extern "C" int function_1(struct test* a1);
689    """
690    generator = code.Generator([analyze_string(body)])
691    self.assertLen(generator.translation_units, 1)
692
693    generator._get_related_types()
694    tu = generator.translation_units[0]
695    tu.get_functions()
696
697    self.assertLen(tu.required_defines, 4)
698    defines = generator._get_defines()
699    self.assertLen(defines, 4)
700    self.assertIn('#define SIZE 1024', defines)
701    self.assertIn('#define SIZE2 2 * 1024', defines)
702    self.assertIn('#define SIZE3 1337', defines)
703    self.assertIn('#define SIZE4 10', defines)
704
705    # Extra check for generation, in case rendering throws error for this test.
706    generator.generate('Test', 'sapi::Tests', None, None)
707
708  def testYaraCase(self):
709    body = """
710      #define YR_ALIGN(n) __attribute__((aligned(n)))
711      #define DECLARE_REFERENCE(type, name) union {    \
712        type name;            \
713        int64_t name##_;      \
714      } YR_ALIGN(8)
715      struct YR_NAMESPACE {
716        int32_t t_flags[1337];
717        DECLARE_REFERENCE(char*, name);
718      };
719
720      extern "C" int function_1(struct YR_NAMESPACE* a1);
721    """
722    generator = code.Generator([analyze_string(body)])
723    self.assertLen(generator.translation_units, 1)
724
725    generator._get_related_types()
726    tu = generator.translation_units[0]
727    tu.get_functions()
728
729    self.assertLen(tu.required_defines, 2)
730    defines = generator._get_defines()
731    # _get_defines will add dependant defines to tu.required_defines
732    self.assertLen(defines, 2)
733    gold = '#define DECLARE_REFERENCE('
734    # DECLARE_REFERENCE must be second to pass this test
735    self.assertTrue(defines[1].startswith(gold))
736
737    # Extra check for generation, in case rendering throws error for this test.
738    generator.generate('Test', 'sapi::Tests', None, None)
739
740  def testDoubleFunction(self):
741    body = """
742      extern "C" int function_1(int a);
743      extern "C" int function_1(int a) {
744        return a + 1;
745      };
746    """
747    generator = code.Generator([analyze_string(body)])
748    self.assertLen(generator.translation_units, 1)
749
750    tu = generator.translation_units[0]
751    tu._process()
752
753    self.assertLen(tu.functions, 1)
754
755    # Extra check for generation, in case rendering throws error for this test.
756    generator.generate('Test', 'sapi::Tests', None, None)
757
758  def testDefineStructBody(self):
759    body = """
760      #define STRUCT_BODY \
761      int a;  \
762      char b; \
763      long c
764      struct test {
765        STRUCT_BODY;
766      };
767      extern "C" void function(struct test* a1);
768    """
769
770    generator = code.Generator([analyze_string(body)])
771    self.assertLen(generator.translation_units, 1)
772
773    # initialize all internal data
774    generator.generate('Test', 'sapi::Tests', None, None)
775    tu = generator.translation_units[0]
776
777    self.assertLen(tu.functions, 1)
778    self.assertLen(tu.required_defines, 1)
779
780  def testJpegTurboCase(self):
781    body = """
782      typedef short JCOEF;
783      #define DCTSIZE2 1024
784      typedef JCOEF JBLOCK[DCTSIZE2];
785
786      extern "C" void function(JBLOCK* a);
787    """
788    generator = code.Generator([analyze_string(body)])
789    self.assertLen(generator.translation_units, 1)
790
791    # initialize all internal data
792    generator.generate('Test', 'sapi::Tests', None, None)
793
794    tu = generator.translation_units[0]
795    self.assertLen(tu.functions, 1)
796    self.assertLen(generator._get_defines(), 1)
797    self.assertLen(generator._get_related_types(), 2)
798
799  def testMultipleTypesWhenConst(self):
800    body = """
801      struct Instance {
802        void* instance = nullptr;
803        void* state_memory = nullptr;
804        void* scratch_memory = nullptr;
805      };
806
807      extern "C" void function1(Instance* a);
808      extern "C" void function2(const Instance* a);
809    """
810    generator = code.Generator([analyze_string(body)])
811    self.assertLen(generator.translation_units, 1)
812
813    # Initialize all internal data
814    generator.generate('Test', 'sapi::Tests', None, None)
815
816    tu = generator.translation_units[0]
817    self.assertLen(tu.functions, 2)
818    self.assertLen(generator._get_related_types(), 1)
819
820  def testReference(self):
821    body = """
822      struct Instance {
823        int a;
824      };
825
826      void Function1(Instance& a, Instance&& a);
827    """
828    generator = code.Generator([analyze_string(body)])
829    self.assertLen(generator.translation_units, 1)
830
831    # Initialize all internal data
832    generator.generate('Test', 'sapi::Tests', None, None)
833
834    tu = generator.translation_units[0]
835    self.assertLen(tu.functions, 1)
836
837    # this will return 0 related types because function will be mangled and
838    # filtered out by generator
839    self.assertEmpty(generator._get_related_types())
840    self.assertLen(next(iter(tu.functions)).get_related_types(), 1)
841
842  def testCppHeader(self):
843    path = 'tmp.h'
844    content = """
845      int sum(int a, float b);
846
847      extern "C" int sum(int a, float b);
848    """
849    unsaved_files = [(path, content)]
850    generator = code.Generator([analyze_strings(path, unsaved_files)])
851    # Initialize all internal data
852    generator.generate('Test', 'sapi::Tests', None, None)
853
854    # generator should filter out mangled function
855    functions = generator._get_functions()
856    self.assertLen(functions, 1)
857
858    tu = generator.translation_units[0]
859    functions = tu.get_functions()
860    self.assertLen(functions, 2)
861
862    mangled_names = [f.cursor.mangled_name for f in functions]
863    self.assertSameElements(mangled_names, ['sum', '_Z3sumif'])
864
865
866if __name__ == '__main__':
867  absltest.main()
868