Home | History | Annotate | Download | only in cindex
      1 import gc
      2 import os
      3 
      4 from clang.cindex import CursorKind
      5 from clang.cindex import Cursor
      6 from clang.cindex import File
      7 from clang.cindex import Index
      8 from clang.cindex import SourceLocation
      9 from clang.cindex import SourceRange
     10 from clang.cindex import TranslationUnitSaveError
     11 from clang.cindex import TranslationUnit
     12 from .util import get_cursor
     13 from .util import get_tu
     14 
     15 kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
     16 
     17 def test_spelling():
     18     path = os.path.join(kInputsDir, 'hello.cpp')
     19     tu = TranslationUnit.from_source(path)
     20     assert tu.spelling == path
     21 
     22 def 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 
     29 def 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 
     36 def 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 
     44 def test_unsaved_files():
     45     tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
     46             ('fake.c', """
     47 #include "fake.h"
     48 int x;
     49 int 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 
     59 def 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 
     66 def 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 
     72 def 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 
     90 def 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 
    108 def 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 
    118 def 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 
    133 def 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 
    154 def 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 
    160 def 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 
    176 def 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 
    192 def 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 
    223 def 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