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