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 TranslationUnit 12from .util import get_cursor 13from .util import get_tu 14 15kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') 16 17def test_spelling(): 18 path = os.path.join(kInputsDir, 'hello.cpp') 19 tu = TranslationUnit.from_source(path) 20 assert tu.spelling == path 21 22def 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 29def 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 36def 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 44def test_unsaved_files(): 45 tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [ 46 ('fake.c', """ 47#include "fake.h" 48int x; 49int 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 59def 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 66def 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 72def 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 90def 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 108def 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 118def 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 133def 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 154def 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 160def 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 176def 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 192def 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 223def 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