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