Home | History | Annotate | Download | only in generate_stubs
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Unittest for the generate_stubs.py.
      7 
      8 Since generate_stubs.py is a code generator, it is hard to do a very good
      9 test.  Instead of creating a golden-file test, which might be flakey, this
     10 test elects instead to verify that various components "exist" within the
     11 generated file as a sanity check.  In particular, there is a simple hit
     12 test to make sure that umbrella functions, etc., do try and include every
     13 function they are responsible for invoking.  Missing an invocation is quite
     14 easily missed.
     15 
     16 There is no attempt to verify ordering of different components, or whether
     17 or not those components are going to parse incorrectly because of prior
     18 errors or positioning.  Most of that should be caught really fast anyways
     19 during any attempt to use a badly behaving script.
     20 """
     21 
     22 import generate_stubs as gs
     23 import re
     24 import StringIO
     25 import sys
     26 import unittest
     27 
     28 
     29 def _MakeSignature(return_type, name, params):
     30   return {'return_type': return_type,
     31           'name': name,
     32           'params': params}
     33 
     34 
     35 SIMPLE_SIGNATURES = [
     36     ('int foo(int a)', _MakeSignature('int', 'foo', ['int a'])),
     37     ('int bar(int a, double b)', _MakeSignature('int', 'bar',
     38                                                 ['int a', 'double b'])),
     39     ('int baz(void)', _MakeSignature('int', 'baz', ['void'])),
     40     ('void quux(void)', _MakeSignature('void', 'quux', ['void'])),
     41     ('void waldo(void);', _MakeSignature('void', 'waldo', ['void'])),
     42     ('int corge(void);', _MakeSignature('int', 'corge', ['void'])),
     43     ]
     44 
     45 TRICKY_SIGNATURES = [
     46     ('const struct name *foo(int a, struct Test* b);  ',
     47      _MakeSignature('const struct name *',
     48                     'foo',
     49                     ['int a', 'struct Test* b'])),
     50     ('const struct name &foo(int a, struct Test* b);',
     51      _MakeSignature('const struct name &',
     52                     'foo',
     53                     ['int a', 'struct Test* b'])),
     54     ('const struct name &_foo(int a, struct Test* b);',
     55      _MakeSignature('const struct name &',
     56                     '_foo',
     57                     ['int a', 'struct Test* b'])),
     58     ('struct name const * const _foo(int a, struct Test* b) '
     59      '__attribute__((inline));',
     60      _MakeSignature('struct name const * const',
     61                     '_foo',
     62                     ['int a', 'struct Test* b']))
     63     ]
     64 
     65 INVALID_SIGNATURES = ['I am bad', 'Seriously bad(', ';;;']
     66 
     67 
     68 class GenerateStubModuleFunctionsUnittest(unittest.TestCase):
     69   def testExtractModuleName(self):
     70     self.assertEqual('somefile-2', gs.ExtractModuleName('somefile-2.ext'))
     71 
     72   def testParseSignatures_EmptyFile(self):
     73     # Empty file just generates empty signatures.
     74     infile = StringIO.StringIO()
     75     signatures = gs.ParseSignatures(infile)
     76     self.assertEqual(0, len(signatures))
     77 
     78   def testParseSignatures_SimpleSignatures(self):
     79     file_contents = '\n'.join([x[0] for x in SIMPLE_SIGNATURES])
     80     infile = StringIO.StringIO(file_contents)
     81     signatures = gs.ParseSignatures(infile)
     82     self.assertEqual(len(SIMPLE_SIGNATURES), len(signatures))
     83 
     84     # We assume signatures are in order.
     85     for i in xrange(len(SIMPLE_SIGNATURES)):
     86       self.assertEqual(SIMPLE_SIGNATURES[i][1], signatures[i],
     87                        msg='Expected %s\nActual %s\nFor %s' %
     88                        (SIMPLE_SIGNATURES[i][1],
     89                         signatures[i],
     90                         SIMPLE_SIGNATURES[i][0]))
     91 
     92   def testParseSignatures_TrickySignatures(self):
     93     file_contents = '\n'.join([x[0] for x in TRICKY_SIGNATURES])
     94     infile = StringIO.StringIO(file_contents)
     95     signatures = gs.ParseSignatures(infile)
     96     self.assertEqual(len(TRICKY_SIGNATURES), len(signatures))
     97 
     98     # We assume signatures are in order.
     99     for i in xrange(len(TRICKY_SIGNATURES)):
    100       self.assertEqual(TRICKY_SIGNATURES[i][1], signatures[i],
    101                        msg='Expected %s\nActual %s\nFor %s' %
    102                        (TRICKY_SIGNATURES[i][1],
    103                         signatures[i],
    104                         TRICKY_SIGNATURES[i][0]))
    105 
    106   def testParseSignatures_InvalidSignatures(self):
    107     for i in INVALID_SIGNATURES:
    108       infile = StringIO.StringIO(i)
    109       self.assertRaises(gs.BadSignatureError, gs.ParseSignatures, infile)
    110 
    111   def testParseSignatures_CommentsIgnored(self):
    112     my_sigs = []
    113     my_sigs.append('# a comment')
    114     my_sigs.append(SIMPLE_SIGNATURES[0][0])
    115     my_sigs.append('# another comment')
    116     my_sigs.append(SIMPLE_SIGNATURES[0][0])
    117     my_sigs.append('# a third comment')
    118     my_sigs.append(SIMPLE_SIGNATURES[0][0])
    119     my_sigs.append('// a fourth comment')
    120     my_sigs.append(SIMPLE_SIGNATURES[0][0])
    121     my_sigs.append('//')
    122     my_sigs.append(SIMPLE_SIGNATURES[0][0])
    123 
    124     file_contents = '\n'.join(my_sigs)
    125     infile = StringIO.StringIO(file_contents)
    126     signatures = gs.ParseSignatures(infile)
    127     self.assertEqual(5, len(signatures))
    128 
    129 
    130 class WindowsLibUnittest(unittest.TestCase):
    131   def testWriteWindowsDefFile(self):
    132     module_name = 'my_module-1'
    133     signatures = [sig[1] for sig in SIMPLE_SIGNATURES]
    134     outfile = StringIO.StringIO()
    135     gs.WriteWindowsDefFile(module_name, signatures, outfile)
    136     contents = outfile.getvalue()
    137 
    138     # Check that the file header is correct.
    139     self.assertTrue(contents.startswith("""LIBRARY %s
    140 EXPORTS
    141 """ % module_name))
    142 
    143     # Check that the signatures were exported.
    144     for sig in signatures:
    145       pattern = '\n  %s\n' % sig['name']
    146       self.assertTrue(re.search(pattern, contents),
    147                       msg='Expected match of "%s" in %s' % (pattern, contents))
    148 
    149   def testQuietRun(self):
    150     output = StringIO.StringIO()
    151     gs.QuietRun([sys.executable,
    152                  '-c', 'print "line 1 and suffix\\nline 2"'],
    153                 write_to=output)
    154     self.assertEqual('line 1 and suffix\nline 2\n', output.getvalue())
    155 
    156     output = StringIO.StringIO()
    157     gs.QuietRun([sys.executable,
    158                  '-c', 'print "line 1 and suffix\\nline 2"'],
    159                  filter='line 1', write_to=output)
    160     self.assertEqual('line 2\n', output.getvalue())
    161 
    162 
    163 class PosixStubWriterUnittest(unittest.TestCase):
    164   def setUp(self):
    165     self.module_name = 'my_module-1'
    166     self.signatures = [sig[1] for sig in SIMPLE_SIGNATURES]
    167     self.out_dir = 'out_dir'
    168     self.writer = gs.PosixStubWriter(self.module_name, '', self.signatures)
    169 
    170   def testEnumName(self):
    171     self.assertEqual('kModuleMy_module1',
    172                      gs.PosixStubWriter.EnumName(self.module_name))
    173 
    174   def testIsInitializedName(self):
    175     self.assertEqual('IsMy_module1Initialized',
    176                      gs.PosixStubWriter.IsInitializedName(self.module_name))
    177 
    178   def testInitializeModuleName(self):
    179     self.assertEqual(
    180         'InitializeMy_module1',
    181         gs.PosixStubWriter.InitializeModuleName(self.module_name))
    182 
    183   def testUninitializeModuleName(self):
    184     self.assertEqual(
    185         'UninitializeMy_module1',
    186         gs.PosixStubWriter.UninitializeModuleName(self.module_name))
    187 
    188   def testStubFunctionPointer(self):
    189     self.assertEqual(
    190         'static int (*foo_ptr)(int a) = NULL;',
    191         gs.PosixStubWriter.StubFunctionPointer(SIMPLE_SIGNATURES[0][1]))
    192 
    193   def testStubFunction(self):
    194     # Test for a signature with a return value and a parameter.
    195     self.assertEqual("""extern int foo(int a) __attribute__((weak));
    196 int  foo(int a) {
    197   return foo_ptr(a);
    198 }""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[0][1]))
    199 
    200     # Test for a signature with a void return value and no parameters.
    201     self.assertEqual("""extern void waldo(void) __attribute__((weak));
    202 void  waldo(void) {
    203   waldo_ptr();
    204 }""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[4][1]))
    205 
    206     # Test export macros.
    207     sig = _MakeSignature('int*', 'foo', ['bool b'])
    208     sig['export'] = 'TEST_EXPORT'
    209     self.assertEqual("""extern int* foo(bool b) __attribute__((weak));
    210 int* TEST_EXPORT foo(bool b) {
    211   return foo_ptr(b);
    212 }""", gs.PosixStubWriter.StubFunction(sig))
    213 
    214   def testWriteImplemenationContents(self):
    215     outfile = StringIO.StringIO()
    216     self.writer.WriteImplementationContents('my_namespace', outfile)
    217     contents = outfile.getvalue()
    218 
    219     # Verify namespace exists somewhere.
    220     self.assertTrue(contents.find('namespace my_namespace {') != -1)
    221 
    222     # Verify that each signature has an _ptr and a function call in the file.
    223     # Check that the signatures were exported.
    224     for sig in self.signatures:
    225       decl = gs.PosixStubWriter.StubFunctionPointer(sig)
    226       self.assertTrue(contents.find(decl) != -1,
    227                       msg='Expected "%s" in %s' % (decl, contents))
    228 
    229     # Verify that each signature has an stub function generated for it.
    230     for sig in self.signatures:
    231       decl = gs.PosixStubWriter.StubFunction(sig)
    232       self.assertTrue(contents.find(decl) != -1,
    233                       msg='Expected "%s" in %s' % (decl, contents))
    234 
    235     # Find module initializer functions.  Make sure all 3 exist.
    236     decl = gs.PosixStubWriter.InitializeModuleName(self.module_name)
    237     self.assertTrue(contents.find(decl) != -1,
    238                     msg='Expected "%s" in %s' % (decl, contents))
    239     decl = gs.PosixStubWriter.UninitializeModuleName(self.module_name)
    240     self.assertTrue(contents.find(decl) != -1,
    241                     msg='Expected "%s" in %s' % (decl, contents))
    242     decl = gs.PosixStubWriter.IsInitializedName(self.module_name)
    243     self.assertTrue(contents.find(decl) != -1,
    244                     msg='Expected "%s" in %s' % (decl, contents))
    245 
    246   def testWriteHeaderContents(self):
    247     # Data for header generation.
    248     module_names = ['oneModule', 'twoModule']
    249 
    250     # Make the header.
    251     outfile = StringIO.StringIO()
    252     self.writer.WriteHeaderContents(module_names, 'my_namespace', 'GUARD_',
    253                                     outfile)
    254     contents = outfile.getvalue()
    255 
    256     # Check for namespace and header guard.
    257     self.assertTrue(contents.find('namespace my_namespace {') != -1)
    258     self.assertTrue(contents.find('#ifndef GUARD_') != -1)
    259 
    260     # Check for umbrella initializer.
    261     self.assertTrue(contents.find('InitializeStubs(') != -1)
    262 
    263     # Check per-module declarations.
    264     for name in module_names:
    265       # Check for enums.
    266       decl = gs.PosixStubWriter.EnumName(name)
    267       self.assertTrue(contents.find(decl) != -1,
    268                       msg='Expected "%s" in %s' % (decl, contents))
    269 
    270       # Check for module initializer functions.
    271       decl = gs.PosixStubWriter.IsInitializedName(name)
    272       self.assertTrue(contents.find(decl) != -1,
    273                       msg='Expected "%s" in %s' % (decl, contents))
    274       decl = gs.PosixStubWriter.InitializeModuleName(name)
    275       self.assertTrue(contents.find(decl) != -1,
    276                       msg='Expected "%s" in %s' % (decl, contents))
    277       decl = gs.PosixStubWriter.UninitializeModuleName(name)
    278       self.assertTrue(contents.find(decl) != -1,
    279                       msg='Expected "%s" in %s' % (decl, contents))
    280 
    281   def testWriteUmbrellaInitializer(self):
    282     # Data for header generation.
    283     module_names = ['oneModule', 'twoModule']
    284 
    285     # Make the header.
    286     outfile = StringIO.StringIO()
    287     self.writer.WriteUmbrellaInitializer(module_names, 'my_namespace', outfile)
    288     contents = outfile.getvalue()
    289 
    290     # Check for umbrella initializer declaration.
    291     self.assertTrue(contents.find('bool InitializeStubs(') != -1)
    292 
    293     # If the umbrella initializer is correctly written, each module will have
    294     # its initializer called, checked, and uninitialized on failure.  Sanity
    295     # check that here.
    296     for name in module_names:
    297       # Check for module initializer functions.
    298       decl = gs.PosixStubWriter.IsInitializedName(name)
    299       self.assertTrue(contents.find(decl) != -1,
    300                       msg='Expected "%s" in %s' % (decl, contents))
    301       decl = gs.PosixStubWriter.InitializeModuleName(name)
    302       self.assertTrue(contents.find(decl) != -1,
    303                       msg='Expected "%s" in %s' % (decl, contents))
    304       decl = gs.PosixStubWriter.UninitializeModuleName(name)
    305       self.assertTrue(contents.find(decl) != -1,
    306                       msg='Expected "%s" in %s' % (decl, contents))
    307 
    308 if __name__ == '__main__':
    309   unittest.main()
    310