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