| import gc |
| import os |
| import tempfile |
| |
| from clang.cindex import CursorKind |
| from clang.cindex import Cursor |
| from clang.cindex import File |
| from clang.cindex import Index |
| from clang.cindex import SourceLocation |
| from clang.cindex import SourceRange |
| from clang.cindex import TranslationUnitSaveError |
| from clang.cindex import TranslationUnitLoadError |
| from clang.cindex import TranslationUnit |
| from .util import get_cursor |
| from .util import get_tu |
| |
| kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') |
| |
| def test_spelling(): |
| path = os.path.join(kInputsDir, 'hello.cpp') |
| tu = TranslationUnit.from_source(path) |
| assert tu.spelling == path |
| |
| def test_cursor(): |
| path = os.path.join(kInputsDir, 'hello.cpp') |
| tu = get_tu(path) |
| c = tu.cursor |
| assert isinstance(c, Cursor) |
| assert c.kind is CursorKind.TRANSLATION_UNIT |
| |
| def test_parse_arguments(): |
| path = os.path.join(kInputsDir, 'parse_arguments.c') |
| tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) |
| spellings = [c.spelling for c in tu.cursor.get_children()] |
| assert spellings[-2] == 'hello' |
| assert spellings[-1] == 'hi' |
| |
| def test_reparse_arguments(): |
| path = os.path.join(kInputsDir, 'parse_arguments.c') |
| tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi']) |
| tu.reparse() |
| spellings = [c.spelling for c in tu.cursor.get_children()] |
| assert spellings[-2] == 'hello' |
| assert spellings[-1] == 'hi' |
| |
| def test_unsaved_files(): |
| tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [ |
| ('fake.c', """ |
| #include "fake.h" |
| int x; |
| int SOME_DEFINE; |
| """), |
| ('./fake.h', """ |
| #define SOME_DEFINE y |
| """) |
| ]) |
| spellings = [c.spelling for c in tu.cursor.get_children()] |
| assert spellings[-2] == 'x' |
| assert spellings[-1] == 'y' |
| |
| def test_unsaved_files_2(): |
| import StringIO |
| tu = TranslationUnit.from_source('fake.c', unsaved_files = [ |
| ('fake.c', StringIO.StringIO('int x;'))]) |
| spellings = [c.spelling for c in tu.cursor.get_children()] |
| assert spellings[-1] == 'x' |
| |
| def normpaths_equal(path1, path2): |
| """ Compares two paths for equality after normalizing them with |
| os.path.normpath |
| """ |
| return os.path.normpath(path1) == os.path.normpath(path2) |
| |
| def test_includes(): |
| def eq(expected, actual): |
| if not actual.is_input_file: |
| return normpaths_equal(expected[0], actual.source.name) and \ |
| normpaths_equal(expected[1], actual.include.name) |
| else: |
| return normpaths_equal(expected[1], actual.include.name) |
| |
| src = os.path.join(kInputsDir, 'include.cpp') |
| h1 = os.path.join(kInputsDir, "header1.h") |
| h2 = os.path.join(kInputsDir, "header2.h") |
| h3 = os.path.join(kInputsDir, "header3.h") |
| inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)] |
| |
| tu = TranslationUnit.from_source(src) |
| for i in zip(inc, tu.get_includes()): |
| assert eq(i[0], i[1]) |
| |
| def save_tu(tu): |
| """Convenience API to save a TranslationUnit to a file. |
| |
| Returns the filename it was saved to. |
| """ |
| _, path = tempfile.mkstemp() |
| tu.save(path) |
| |
| return path |
| |
| def test_save(): |
| """Ensure TranslationUnit.save() works.""" |
| |
| tu = get_tu('int foo();') |
| |
| path = save_tu(tu) |
| assert os.path.exists(path) |
| assert os.path.getsize(path) > 0 |
| os.unlink(path) |
| |
| def test_save_translation_errors(): |
| """Ensure that saving to an invalid directory raises.""" |
| |
| tu = get_tu('int foo();') |
| |
| path = '/does/not/exist/llvm-test.ast' |
| assert not os.path.exists(os.path.dirname(path)) |
| |
| try: |
| tu.save(path) |
| assert False |
| except TranslationUnitSaveError as ex: |
| expected = TranslationUnitSaveError.ERROR_UNKNOWN |
| assert ex.save_error == expected |
| |
| def test_load(): |
| """Ensure TranslationUnits can be constructed from saved files.""" |
| |
| tu = get_tu('int foo();') |
| assert len(tu.diagnostics) == 0 |
| path = save_tu(tu) |
| |
| assert os.path.exists(path) |
| assert os.path.getsize(path) > 0 |
| |
| tu2 = TranslationUnit.from_ast_file(filename=path) |
| assert len(tu2.diagnostics) == 0 |
| |
| foo = get_cursor(tu2, 'foo') |
| assert foo is not None |
| |
| # Just in case there is an open file descriptor somewhere. |
| del tu2 |
| |
| os.unlink(path) |
| |
| def test_index_parse(): |
| path = os.path.join(kInputsDir, 'hello.cpp') |
| index = Index.create() |
| tu = index.parse(path) |
| assert isinstance(tu, TranslationUnit) |
| |
| def test_get_file(): |
| """Ensure tu.get_file() works appropriately.""" |
| |
| tu = get_tu('int foo();') |
| |
| f = tu.get_file('t.c') |
| assert isinstance(f, File) |
| assert f.name == 't.c' |
| |
| try: |
| f = tu.get_file('foobar.cpp') |
| except: |
| pass |
| else: |
| assert False |
| |
| def test_get_source_location(): |
| """Ensure tu.get_source_location() works.""" |
| |
| tu = get_tu('int foo();') |
| |
| location = tu.get_location('t.c', 2) |
| assert isinstance(location, SourceLocation) |
| assert location.offset == 2 |
| assert location.file.name == 't.c' |
| |
| location = tu.get_location('t.c', (1, 3)) |
| assert isinstance(location, SourceLocation) |
| assert location.line == 1 |
| assert location.column == 3 |
| assert location.file.name == 't.c' |
| |
| def test_get_source_range(): |
| """Ensure tu.get_source_range() works.""" |
| |
| tu = get_tu('int foo();') |
| |
| r = tu.get_extent('t.c', (1,4)) |
| assert isinstance(r, SourceRange) |
| assert r.start.offset == 1 |
| assert r.end.offset == 4 |
| assert r.start.file.name == 't.c' |
| assert r.end.file.name == 't.c' |
| |
| r = tu.get_extent('t.c', ((1,2), (1,3))) |
| assert isinstance(r, SourceRange) |
| assert r.start.line == 1 |
| assert r.start.column == 2 |
| assert r.end.line == 1 |
| assert r.end.column == 3 |
| assert r.start.file.name == 't.c' |
| assert r.end.file.name == 't.c' |
| |
| start = tu.get_location('t.c', 0) |
| end = tu.get_location('t.c', 5) |
| |
| r = tu.get_extent('t.c', (start, end)) |
| assert isinstance(r, SourceRange) |
| assert r.start.offset == 0 |
| assert r.end.offset == 5 |
| assert r.start.file.name == 't.c' |
| assert r.end.file.name == 't.c' |
| |
| def test_get_tokens_gc(): |
| """Ensures get_tokens() works properly with garbage collection.""" |
| |
| tu = get_tu('int foo();') |
| r = tu.get_extent('t.c', (0, 10)) |
| tokens = list(tu.get_tokens(extent=r)) |
| |
| assert tokens[0].spelling == 'int' |
| gc.collect() |
| assert tokens[0].spelling == 'int' |
| |
| del tokens[1] |
| gc.collect() |
| assert tokens[0].spelling == 'int' |
| |
| # May trigger segfault if we don't do our job properly. |
| del tokens |
| gc.collect() |
| gc.collect() # Just in case. |
| |
| def test_fail_from_source(): |
| path = os.path.join(kInputsDir, 'non-existent.cpp') |
| try: |
| tu = TranslationUnit.from_source(path) |
| except TranslationUnitLoadError: |
| tu = None |
| assert tu == None |
| |
| def test_fail_from_ast_file(): |
| path = os.path.join(kInputsDir, 'non-existent.ast') |
| try: |
| tu = TranslationUnit.from_ast_file(path) |
| except TranslationUnitLoadError: |
| tu = None |
| assert tu == None |