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