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