1#!/usr/bin/env python3 2 3# Copyright 2018 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6"""Tests for extract_sqlite_api.py. 7 8These tests should be getting picked up by the PRESUBMIT.py in this directory. 9""" 10 11from importlib.machinery import SourceFileLoader 12import os 13import shutil 14import sys 15import tempfile 16import unittest 17 18class ExtractSqliteApiUnittest(unittest.TestCase): 19 def setUp(self): 20 self.test_root = tempfile.mkdtemp() 21 source_path = os.path.join( 22 os.path.dirname(os.path.realpath(__file__)), 'extract_sqlite_api.py') 23 self.extractor = SourceFileLoader('extract_api', source_path).load_module() 24 25 def tearDown(self): 26 if self.test_root: 27 shutil.rmtree(self.test_root) 28 29 def testExtractLineTuples(self): 30 golden = [(1, 'Line1'), (2, ''), (3, 'Line 2'), (4, 'Line3'), (5, '')] 31 text_with_newline = "Line1\n\nLine 2 \nLine3\n" 32 self.assertEqual(self.extractor.ExtractLineTuples(text_with_newline), 33 golden) 34 35 golden = [(1, 'Line1'), (2, ''), (3, 'Line 2'), (4, 'Line3')] 36 text_without_newline = "Line1\n\nLine 2 \nLine3" 37 self.assertEqual(self.extractor.ExtractLineTuples(text_without_newline), 38 golden) 39 40 def testExtractPreprocessorDirectives(self): 41 lines = [ 42 (1, '// Header comment'), 43 (2, '#define DIRECTIVE 1'), 44 (3, 'int main() { // \\'), 45 (4, '}'), 46 (5, ''), 47 (6, '#define MULTILINE \\'), 48 (7, 'MORE_MULTILINE_DIRECTIVE\\'), 49 (8, 'END_MULTILINE_DIRECTIVE'), 50 (9, 'void code() { }'), 51 ] 52 53 directives, code_lines = self.extractor.ExtractPreprocessorDirectives(lines) 54 self.assertEqual(directives, [ 55 '#define DIRECTIVE 1', 56 '#define MULTILINE \nMORE_MULTILINE_DIRECTIVE\nEND_MULTILINE_DIRECTIVE', 57 ]) 58 self.assertEqual(code_lines, [ 59 (1, '// Header comment'), 60 (3, 'int main() { // \\'), 61 (4, '}'), 62 (5, ''), 63 (9, 'void code() { }'), 64 ]) 65 66 def testExtractDefineMacroName(self): 67 self.assertEqual( 68 'SQLITE_API', self.extractor.ExtractDefineMacroName( 69 '#define SQLITE_API 1')) 70 self.assertEqual( 71 'SQLITE_API', self.extractor.ExtractDefineMacroName( 72 '#define SQLITE_API')) 73 self.assertEqual( 74 'SQLITE_API', self.extractor.ExtractDefineMacroName( 75 '#define SQLITE_API\n1')) 76 self.assertEqual( 77 'SQLITE_API', self.extractor.ExtractDefineMacroName( 78 '# define SQLITE_API 1')) 79 self.assertEqual( 80 'SQLITE_API', self.extractor.ExtractDefineMacroName( 81 '#\tdefine\tSQLITE_API\t1')) 82 self.assertEqual( 83 None, self.extractor.ExtractDefineMacroName( 84 ' #define SQLITE_API 1')) 85 self.assertEqual( 86 None, self.extractor.ExtractDefineMacroName( 87 ' #define SQLITE_API() 1')) 88 self.assertEqual(None, self.extractor.ExtractDefineMacroName('')) 89 90 def testRemoveLineComments(self): 91 self.assertEqual( 92 'word;', self.extractor.RemoveLineComments('word;')) 93 self.assertEqual( 94 '', self.extractor.RemoveLineComments('')) 95 self.assertEqual( 96 '', self.extractor.RemoveLineComments('// comment')) 97 self.assertEqual( 98 '', self.extractor.RemoveLineComments('/* comment */')) 99 self.assertEqual( 100 'word;', self.extractor.RemoveLineComments('wo/*comment*/rd;')) 101 self.assertEqual( 102 'word;*/', self.extractor.RemoveLineComments('wo/*comment*/rd;*/')) 103 self.assertEqual( 104 'word;*/', self.extractor.RemoveLineComments('wo/*/*comment*/rd;*/')) 105 self.assertEqual( 106 'word;', self.extractor.RemoveLineComments('wo/*comm//ent*/rd;')) 107 108 def testRemoveComments(self): 109 lines = [ 110 (1, 'code();'), 111 (2, 'more_code(); /* with comment */ more_code();'), 112 (3, '/**'), 113 (4, 'Spec text'), 114 (5, '**/ spec_code();'), 115 (6, 'late_code(); /* with comment */ more_late_code(); /* late comment'), 116 (7, 'ends here // C++ trap */ code(); // /* C trap'), 117 (8, 'last_code();'), 118 ] 119 120 self.assertEqual(self.extractor.RemoveComments(lines), [ 121 (1, 'code();'), 122 (2, 'more_code(); more_code();'), 123 (3, ''), 124 (5, ' spec_code();'), 125 (6, 'late_code(); more_late_code(); '), 126 (7, ' code(); '), 127 (8, 'last_code();'), 128 ]) 129 130 def testToStatementTuples(self): 131 lines = [ 132 (1, 'void function();'), 133 (2, 'int main('), 134 (3, ' int argc, char* argv) {'), 135 (4, ' statement1; statement2;'), 136 (5, '}'), 137 (6, 'stat'), 138 (7, 'ement4; statement5; sta'), 139 (8, 'tem'), 140 (9, 'ent6; statement7;') 141 ] 142 143 self.assertEqual(self.extractor.ToStatementTuples(lines), [ 144 (1, 1, 'void function()'), 145 (2, 3, 'int main(\n int argc, char* argv)'), 146 (4, 4, 'statement1'), 147 (4, 4, 'statement2'), 148 (5, 5, ''), 149 (6, 7, 'stat\nement4'), 150 (7, 7, 'statement5'), 151 (7, 9, 'sta\ntem\nent6'), 152 (9, 9, 'statement7'), 153 ]) 154 155 def testExtractApiExport(self): 156 self.assertEqual( 157 'sqlite3_init', 158 self.extractor.ExtractApiExport( 159 set(), 'SQLITE_API', 'SQLITE_API void sqlite3_init()')) 160 self.assertEqual( 161 'sqlite3_sleep', 162 self.extractor.ExtractApiExport( 163 set(), 'SQLITE_API', 'SQLITE_API int sqlite3_sleep(int ms)')) 164 self.assertEqual( 165 'sqlite3_sleep', 166 self.extractor.ExtractApiExport( 167 set(), 'SQLITE_API', 168 'SQLITE_API long long sqlite3_sleep(int ms)')) 169 self.assertEqual( 170 'sqlite3rbu_temp_size', 171 self.extractor.ExtractApiExport( 172 set(), 'SQLITE_API', 173 'SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu)')) 174 self.assertEqual( 175 'sqlite3_expired', 176 self.extractor.ExtractApiExport( 177 set(['SQLITE_DEPRECATED']), 'SQLITE_API', 178 'SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*)')) 179 # SQLite's header actually #defines double (in some cases). 180 self.assertEqual( 181 'sqlite3_column_double', 182 self.extractor.ExtractApiExport( 183 set(['double']), 'SQLITE_API', 184 'SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol)')) 185 self.assertEqual( 186 'sqlite3_temp_directory', 187 self.extractor.ExtractApiExport( 188 set(['SQLITE_EXTERN']), 'SQLITE_API', 189 'SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory')) 190 self.assertEqual( 191 'sqlite3_version', 192 self.extractor.ExtractApiExport( 193 set(['SQLITE_EXTERN']), 'SQLITE_API', 194 'SQLITE_API SQLITE_EXTERN const char sqlite3_version[]')) 195 self.assertEqual( 196 None, 197 self.extractor.ExtractApiExport( 198 set(['SQLITE_DEPRECATED']), 'SQLITE_API', 199 'NOT_SQLITE_API struct sqlite_type sqlite3_sleep(int ms)')) 200 201 with self.assertRaisesRegex(self.extractor.ExtractError, 202 'Mixed simple .* and composite'): 203 self.extractor.ExtractApiExport( 204 set(), 'SQLITE_API', 'SQLITE_API void int sqlite3_sleep(int ms)') 205 with self.assertRaisesRegex(self.extractor.ExtractError, 206 'Unsupported keyword struct'): 207 self.extractor.ExtractApiExport( 208 set(), 'SQLITE_API', 209 'SQLITE_API struct sqlite_type sqlite3_sleep(int ms)') 210 with self.assertRaisesRegex(self.extractor.ExtractError, 211 'int\+\+ parsed as type name'): 212 self.extractor.ExtractApiExport( 213 set(), 'SQLITE_API', 'SQLITE_API int++ sqlite3_sleep(int ms)') 214 with self.assertRaisesRegex(self.extractor.ExtractError, 215 'sqlite3\+sleep parsed as symbol'): 216 self.extractor.ExtractApiExport( 217 set(), 'SQLITE_API', 'SQLITE_API int sqlite3+sleep(int ms)') 218 219 def testExportedSymbolLine(self): 220 self.assertEqual( 221 '#define sqlite3_sleep chrome_sqlite3_sleep // Line 42', 222 self.extractor.ExportedSymbolLine( 223 'chrome_', 'sqlite3_sleep', 224 (42, 42, 'SQLITE_API int chrome_sqlite3_sleep(int ms)'))) 225 self.assertEqual( 226 '#define sqlite3_sleep chrome_sqlite3_sleep // Lines 42-44', 227 self.extractor.ExportedSymbolLine( 228 'chrome_', 'sqlite3_sleep', 229 (42, 44, 'SQLITE_API int chrome_sqlite3_sleep(int ms)'))) 230 231 def testExportedExceptionLine(self): 232 self.assertEqual( 233 '// TODO: Lines 42-44 -- Something went wrong', 234 self.extractor.ExportedExceptionLine( 235 self.extractor.ExtractError('Something went wrong'), 236 (42, 44, 'SQLITE_API int chrome_sqlite3_sleep(int ms)'))) 237 238 def testProcessSource(self): 239 file_content = '\n'.join([ 240 '/*', 241 'struct sqlite_type sqlite3_sleep; // Remove comments', 242 '*/', 243 '#define SQLITE_DEPRECATED', 244 'SQLITE_API int sqlite3_sleep(int ms);', 245 'SQLITE_API struct sqlite_type sqlite3_sleep(int ms);', 246 'SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);', 247 ]) 248 golden_output = [ 249 '// Header', 250 '#define sqlite3_expired chrome_sqlite3_expired // Line 7', 251 '#define sqlite3_sleep chrome_sqlite3_sleep // Line 5', 252 '// TODO: Lines 6-6 -- Unsupported keyword struct', 253 '// Footer', 254 ] 255 self.assertEqual( 256 golden_output, 257 self.extractor.ProcessSource('SQLITE_API', 'chrome_', '// Header', 258 '// Footer', file_content)) 259 260 def testProcessSourceFile(self): 261 file_content = '\n'.join([ 262 '/*', 263 'struct sqlite_type sqlite3_sleep; // Remove comments', 264 '*/', 265 '#define SQLITE_DEPRECATED', 266 'SQLITE_API int sqlite3_sleep(int ms);', 267 'SQLITE_API struct sqlite_type sqlite3_sleep(int ms);', 268 'SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);', 269 ]) 270 golden_output = '\n'.join([ 271 '// Header', 272 '#define sqlite3_expired chrome_sqlite3_expired // Line 7', 273 '#define sqlite3_sleep chrome_sqlite3_sleep // Line 5', 274 '// TODO: Lines 6-6 -- Unsupported keyword struct', 275 '// Footer', 276 '', 277 ]) 278 279 input_file = os.path.join(self.test_root, 'input.h') 280 output_file = os.path.join(self.test_root, 'macros.h') 281 with open(input_file, 'w') as f: 282 f.write(file_content) 283 self.extractor.ProcessSourceFile( 284 'SQLITE_API', 'chrome_', '// Header', '// Footer', input_file, 285 output_file) 286 with open(output_file, 'r') as f: 287 self.assertEqual(f.read(), golden_output) 288 289if __name__ == '__main__': 290 unittest.main() 291