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