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