Home | History | Annotate | Download | only in cindex
      1 import gc
      2 import os
      3 import tempfile
      4 
      5 from clang.cindex import CursorKind
      6 from clang.cindex import Cursor
      7 from clang.cindex import File
      8 from clang.cindex import Index
      9 from clang.cindex import SourceLocation
     10 from clang.cindex import SourceRange
     11 from clang.cindex import TranslationUnitSaveError
     12 from clang.cindex import TranslationUnitLoadError
     13 from clang.cindex import TranslationUnit
     14 from .util import get_cursor
     15 from .util import get_tu
     16 
     17 kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
     18 
     19 def test_spelling():
     20     path = os.path.join(kInputsDir, 'hello.cpp')
     21     tu = TranslationUnit.from_source(path)
     22     assert tu.spelling == path
     23 
     24 def 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 
     31 def 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 
     38 def 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 
     46 def test_unsaved_files():
     47     tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
     48             ('fake.c', """
     49 #include "fake.h"
     50 int x;
     51 int 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 
     61 def 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 
     68 def 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 
     74 def 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 
     92 def 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 
    102 def 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 
    112 def 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 
    127 def 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 
    148 def 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 
    154 def 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 
    170 def 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 
    186 def 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 
    217 def 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 
    237 def 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 
    245 def 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