1import os 2from clang.cindex import Config 3if 'CLANG_LIBRARY_PATH' in os.environ: 4 Config.set_library_path(os.environ['CLANG_LIBRARY_PATH']) 5 6from clang.cindex import CompilationDatabase 7from clang.cindex import CompilationDatabaseError 8from clang.cindex import CompileCommands 9from clang.cindex import CompileCommand 10import os 11import gc 12import unittest 13import sys 14from .util import skip_if_no_fspath 15from .util import str_to_path 16 17 18kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') 19 20 21@unittest.skipIf(sys.platform == 'win32', "TODO: Fix these tests on Windows") 22class TestCDB(unittest.TestCase): 23 def test_create_fail(self): 24 """Check we fail loading a database with an assertion""" 25 path = os.path.dirname(__file__) 26 27 # clang_CompilationDatabase_fromDirectory calls fprintf(stderr, ...) 28 # Suppress its output. 29 stderr = os.dup(2) 30 with open(os.devnull, 'wb') as null: 31 os.dup2(null.fileno(), 2) 32 with self.assertRaises(CompilationDatabaseError) as cm: 33 cdb = CompilationDatabase.fromDirectory(path) 34 os.dup2(stderr, 2) 35 os.close(stderr) 36 37 e = cm.exception 38 self.assertEqual(e.cdb_error, 39 CompilationDatabaseError.ERROR_CANNOTLOADDATABASE) 40 41 def test_create(self): 42 """Check we can load a compilation database""" 43 cdb = CompilationDatabase.fromDirectory(kInputsDir) 44 45 def test_lookup_succeed(self): 46 """Check we get some results if the file exists in the db""" 47 cdb = CompilationDatabase.fromDirectory(kInputsDir) 48 cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') 49 self.assertNotEqual(len(cmds), 0) 50 51 @skip_if_no_fspath 52 def test_lookup_succeed_pathlike(self): 53 """Same as test_lookup_succeed, but with PathLikes""" 54 cdb = CompilationDatabase.fromDirectory(str_to_path(kInputsDir)) 55 cmds = cdb.getCompileCommands(str_to_path('/home/john.doe/MyProject/project.cpp')) 56 self.assertNotEqual(len(cmds), 0) 57 58 def test_all_compilecommand(self): 59 """Check we get all results from the db""" 60 cdb = CompilationDatabase.fromDirectory(kInputsDir) 61 cmds = cdb.getAllCompileCommands() 62 self.assertEqual(len(cmds), 3) 63 expected = [ 64 { 'wd': '/home/john.doe/MyProject', 65 'file': '/home/john.doe/MyProject/project.cpp', 66 'line': ['clang++', '--driver-mode=g++', '-o', 'project.o', '-c', 67 '/home/john.doe/MyProject/project.cpp']}, 68 { 'wd': '/home/john.doe/MyProjectA', 69 'file': '/home/john.doe/MyProject/project2.cpp', 70 'line': ['clang++', '--driver-mode=g++', '-o', 'project2.o', '-c', 71 '/home/john.doe/MyProject/project2.cpp']}, 72 { 'wd': '/home/john.doe/MyProjectB', 73 'file': '/home/john.doe/MyProject/project2.cpp', 74 'line': ['clang++', '--driver-mode=g++', '-DFEATURE=1', '-o', 75 'project2-feature.o', '-c', 76 '/home/john.doe/MyProject/project2.cpp']}, 77 78 ] 79 for i in range(len(cmds)): 80 self.assertEqual(cmds[i].directory, expected[i]['wd']) 81 self.assertEqual(cmds[i].filename, expected[i]['file']) 82 for arg, exp in zip(cmds[i].arguments, expected[i]['line']): 83 self.assertEqual(arg, exp) 84 85 def test_1_compilecommand(self): 86 """Check file with single compile command""" 87 cdb = CompilationDatabase.fromDirectory(kInputsDir) 88 file = '/home/john.doe/MyProject/project.cpp' 89 cmds = cdb.getCompileCommands(file) 90 self.assertEqual(len(cmds), 1) 91 self.assertEqual(cmds[0].directory, os.path.dirname(file)) 92 self.assertEqual(cmds[0].filename, file) 93 expected = [ 'clang++', '--driver-mode=g++', '-o', 'project.o', '-c', 94 '/home/john.doe/MyProject/project.cpp'] 95 for arg, exp in zip(cmds[0].arguments, expected): 96 self.assertEqual(arg, exp) 97 98 def test_2_compilecommand(self): 99 """Check file with 2 compile commands""" 100 cdb = CompilationDatabase.fromDirectory(kInputsDir) 101 cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp') 102 self.assertEqual(len(cmds), 2) 103 expected = [ 104 { 'wd': '/home/john.doe/MyProjectA', 105 'line': ['clang++', '--driver-mode=g++', '-o', 'project2.o', '-c', 106 '/home/john.doe/MyProject/project2.cpp']}, 107 { 'wd': '/home/john.doe/MyProjectB', 108 'line': ['clang++', '--driver-mode=g++', '-DFEATURE=1', '-o', 109 'project2-feature.o', '-c', 110 '/home/john.doe/MyProject/project2.cpp']} 111 ] 112 for i in range(len(cmds)): 113 self.assertEqual(cmds[i].directory, expected[i]['wd']) 114 for arg, exp in zip(cmds[i].arguments, expected[i]['line']): 115 self.assertEqual(arg, exp) 116 117 def test_compilecommand_iterator_stops(self): 118 """Check that iterator stops after the correct number of elements""" 119 cdb = CompilationDatabase.fromDirectory(kInputsDir) 120 count = 0 121 for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'): 122 count += 1 123 self.assertLessEqual(count, 2) 124 125 def test_compilationDB_references(self): 126 """Ensure CompilationsCommands are independent of the database""" 127 cdb = CompilationDatabase.fromDirectory(kInputsDir) 128 cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') 129 del cdb 130 gc.collect() 131 workingdir = cmds[0].directory 132 133 def test_compilationCommands_references(self): 134 """Ensure CompilationsCommand keeps a reference to CompilationCommands""" 135 cdb = CompilationDatabase.fromDirectory(kInputsDir) 136 cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp') 137 del cdb 138 cmd0 = cmds[0] 139 del cmds 140 gc.collect() 141 workingdir = cmd0.directory 142