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