1import gc 2import os 3 4from clang.cindex import CursorKind 5from clang.cindex import Cursor 6from clang.cindex import File 7from clang.cindex import Index 8from clang.cindex import SourceLocation 9from clang.cindex import SourceRange 10from clang.cindex import TranslationUnitSaveError 11from clang.cindex import TranslationUnitLoadError 12from clang.cindex import TranslationUnit 13from .util import get_cursor 14from .util import get_tu 15 16kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') 17 18def test_spelling(): 19 path = os.path.join(kInputsDir, 'hello.cpp') 20 tu = TranslationUnit.from_source(path) 21 assert tu.spelling == path 22 23def 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 30def 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 37def 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 45def test_unsaved_files(): 46 tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [ 47 ('fake.c', """ 48#include "fake.h" 49int x; 50int 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 60def 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 67def 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 73def 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 91def 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 109def 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 119def 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 134def 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 155def 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 161def 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 177def 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 193def 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 224def 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 244def 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 252def 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