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