1 import gc 2 import os 3 import tempfile 4 5 from clang.cindex import CursorKind 6 from clang.cindex import Cursor 7 from clang.cindex import File 8 from clang.cindex import Index 9 from clang.cindex import SourceLocation 10 from clang.cindex import SourceRange 11 from clang.cindex import TranslationUnitSaveError 12 from clang.cindex import TranslationUnitLoadError 13 from clang.cindex import TranslationUnit 14 from .util import get_cursor 15 from .util import get_tu 16 17 kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') 18 19 def test_spelling(): 20 path = os.path.join(kInputsDir, 'hello.cpp') 21 tu = TranslationUnit.from_source(path) 22 assert tu.spelling == path 23 24 def test_cursor(): 25 path = os.path.join(kInputsDir, 'hello.cpp') 26 tu = get_tu(path) 27 c = tu.cursor 28 assert isinstance(c, Cursor) 29 assert c.kind is CursorKind.TRANSLATION_UNIT 30 31 def test_parse_arguments(): 32 path = os.path.join(kInputsDir, 'parse_arguments.c') 33 tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) 34 spellings = [c.spelling for c in tu.cursor.get_children()] 35 assert spellings[-2] == 'hello' 36 assert spellings[-1] == 'hi' 37 38 def test_reparse_arguments(): 39 path = os.path.join(kInputsDir, 'parse_arguments.c') 40 tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) 41 tu.reparse() 42 spellings = [c.spelling for c in tu.cursor.get_children()] 43 assert spellings[-2] == 'hello' 44 assert spellings[-1] == 'hi' 45 46 def test_unsaved_files(): 47 tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [ 48 ('fake.c', """ 49 #include "fake.h" 50 int x; 51 int SOME_DEFINE; 52 """), 53 ('./fake.h', """ 54 #define SOME_DEFINE y 55 """) 56 ]) 57 spellings = [c.spelling for c in tu.cursor.get_children()] 58 assert spellings[-2] == 'x' 59 assert spellings[-1] == 'y' 60 61 def test_unsaved_files_2(): 62 import StringIO 63 tu = TranslationUnit.from_source('fake.c', unsaved_files = [ 64 ('fake.c', StringIO.StringIO('int x;'))]) 65 spellings = [c.spelling for c in tu.cursor.get_children()] 66 assert spellings[-1] == 'x' 67 68 def normpaths_equal(path1, path2): 69 """ Compares two paths for equality after normalizing them with 70 os.path.normpath 71 """ 72 return os.path.normpath(path1) == os.path.normpath(path2) 73 74 def test_includes(): 75 def eq(expected, actual): 76 if not actual.is_input_file: 77 return normpaths_equal(expected[0], actual.source.name) and \ 78 normpaths_equal(expected[1], actual.include.name) 79 else: 80 return normpaths_equal(expected[1], actual.include.name) 81 82 src = os.path.join(kInputsDir, 'include.cpp') 83 h1 = os.path.join(kInputsDir, "header1.h") 84 h2 = os.path.join(kInputsDir, "header2.h") 85 h3 = os.path.join(kInputsDir, "header3.h") 86 inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)] 87 88 tu = TranslationUnit.from_source(src) 89 for i in zip(inc, tu.get_includes()): 90 assert eq(i[0], i[1]) 91 92 def save_tu(tu): 93 """Convenience API to save a TranslationUnit to a file. 94 95 Returns the filename it was saved to. 96 """ 97 _, path = tempfile.mkstemp() 98 tu.save(path) 99 100 return path 101 102 def test_save(): 103 """Ensure TranslationUnit.save() works.""" 104 105 tu = get_tu('int foo();') 106 107 path = save_tu(tu) 108 assert os.path.exists(path) 109 assert os.path.getsize(path) > 0 110 os.unlink(path) 111 112 def test_save_translation_errors(): 113 """Ensure that saving to an invalid directory raises.""" 114 115 tu = get_tu('int foo();') 116 117 path = '/does/not/exist/llvm-test.ast' 118 assert not os.path.exists(os.path.dirname(path)) 119 120 try: 121 tu.save(path) 122 assert False 123 except TranslationUnitSaveError as ex: 124 expected = TranslationUnitSaveError.ERROR_UNKNOWN 125 assert ex.save_error == expected 126 127 def test_load(): 128 """Ensure TranslationUnits can be constructed from saved files.""" 129 130 tu = get_tu('int foo();') 131 assert len(tu.diagnostics) == 0 132 path = save_tu(tu) 133 134 assert os.path.exists(path) 135 assert os.path.getsize(path) > 0 136 137 tu2 = TranslationUnit.from_ast_file(filename=path) 138 assert len(tu2.diagnostics) == 0 139 140 foo = get_cursor(tu2, 'foo') 141 assert foo is not None 142 143 # Just in case there is an open file descriptor somewhere. 144 del tu2 145 146 os.unlink(path) 147 148 def test_index_parse(): 149 path = os.path.join(kInputsDir, 'hello.cpp') 150 index = Index.create() 151 tu = index.parse(path) 152 assert isinstance(tu, TranslationUnit) 153 154 def test_get_file(): 155 """Ensure tu.get_file() works appropriately.""" 156 157 tu = get_tu('int foo();') 158 159 f = tu.get_file('t.c') 160 assert isinstance(f, File) 161 assert f.name == 't.c' 162 163 try: 164 f = tu.get_file('foobar.cpp') 165 except: 166 pass 167 else: 168 assert False 169 170 def test_get_source_location(): 171 """Ensure tu.get_source_location() works.""" 172 173 tu = get_tu('int foo();') 174 175 location = tu.get_location('t.c', 2) 176 assert isinstance(location, SourceLocation) 177 assert location.offset == 2 178 assert location.file.name == 't.c' 179 180 location = tu.get_location('t.c', (1, 3)) 181 assert isinstance(location, SourceLocation) 182 assert location.line == 1 183 assert location.column == 3 184 assert location.file.name == 't.c' 185 186 def test_get_source_range(): 187 """Ensure tu.get_source_range() works.""" 188 189 tu = get_tu('int foo();') 190 191 r = tu.get_extent('t.c', (1,4)) 192 assert isinstance(r, SourceRange) 193 assert r.start.offset == 1 194 assert r.end.offset == 4 195 assert r.start.file.name == 't.c' 196 assert r.end.file.name == 't.c' 197 198 r = tu.get_extent('t.c', ((1,2), (1,3))) 199 assert isinstance(r, SourceRange) 200 assert r.start.line == 1 201 assert r.start.column == 2 202 assert r.end.line == 1 203 assert r.end.column == 3 204 assert r.start.file.name == 't.c' 205 assert r.end.file.name == 't.c' 206 207 start = tu.get_location('t.c', 0) 208 end = tu.get_location('t.c', 5) 209 210 r = tu.get_extent('t.c', (start, end)) 211 assert isinstance(r, SourceRange) 212 assert r.start.offset == 0 213 assert r.end.offset == 5 214 assert r.start.file.name == 't.c' 215 assert r.end.file.name == 't.c' 216 217 def test_get_tokens_gc(): 218 """Ensures get_tokens() works properly with garbage collection.""" 219 220 tu = get_tu('int foo();') 221 r = tu.get_extent('t.c', (0, 10)) 222 tokens = list(tu.get_tokens(extent=r)) 223 224 assert tokens[0].spelling == 'int' 225 gc.collect() 226 assert tokens[0].spelling == 'int' 227 228 del tokens[1] 229 gc.collect() 230 assert tokens[0].spelling == 'int' 231 232 # May trigger segfault if we don't do our job properly. 233 del tokens 234 gc.collect() 235 gc.collect() # Just in case. 236 237 def test_fail_from_source(): 238 path = os.path.join(kInputsDir, 'non-existent.cpp') 239 try: 240 tu = TranslationUnit.from_source(path) 241 except TranslationUnitLoadError: 242 tu = None 243 assert tu == None 244 245 def test_fail_from_ast_file(): 246 path = os.path.join(kInputsDir, 'non-existent.ast') 247 try: 248 tu = TranslationUnit.from_ast_file(path) 249 except TranslationUnitLoadError: 250 tu = None 251 assert tu == None 252