Skip to content

Commit ea568a9

Browse files
authored
[libclang/python] Improve test coverage (#109846)
Achieve 100% test coverage on classes Cursor, Diagnostic, Type.
1 parent 51259de commit ea568a9

File tree

3 files changed

+249
-4
lines changed

3 files changed

+249
-4
lines changed

clang/bindings/python/tests/cindex/test_cursor.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from clang.cindex import TranslationUnit
1515
from clang.cindex import TypeKind
1616
from clang.cindex import BinaryOperator
17+
from clang.cindex import StorageClass
1718
from .util import get_cursor
1819
from .util import get_cursors
1920
from .util import get_tu
@@ -279,6 +280,90 @@ def test_is_default_method(self):
279280
self.assertTrue(xc.is_default_method())
280281
self.assertFalse(yc.is_default_method())
281282

283+
def test_is_deleted_method(self):
284+
source = "class X { X() = delete; }; class Y { Y(); };"
285+
tu = get_tu(source, lang="cpp")
286+
287+
xs = get_cursors(tu, "X")
288+
ys = get_cursors(tu, "Y")
289+
290+
self.assertEqual(len(xs), 2)
291+
self.assertEqual(len(ys), 2)
292+
293+
xc = xs[1]
294+
yc = ys[1]
295+
296+
self.assertTrue(xc.is_deleted_method())
297+
self.assertFalse(yc.is_deleted_method())
298+
299+
def test_is_copy_assignment_operator_method(self):
300+
source_with_copy_assignment_operators = """
301+
struct Foo {
302+
// Those are copy-assignment operators
303+
bool operator=(const Foo&);
304+
bool operator=(Foo&);
305+
Foo operator=(Foo);
306+
bool operator=(volatile Foo&);
307+
bool operator=(const volatile Foo&);
308+
309+
// Positive-check that the recognition works for templated classes too
310+
template <typename T>
311+
class Bar {
312+
bool operator=(const Bar&);
313+
Bar operator=(const Bar);
314+
bool operator=(Bar<T>&);
315+
bool operator=(volatile Bar&);
316+
bool operator=(const volatile Bar<T>&);
317+
};
318+
"""
319+
source_without_copy_assignment_operators = """
320+
struct Foo {
321+
// Those are not copy-assignment operators
322+
template<typename T>
323+
bool operator=(const T&);
324+
bool operator=(const bool&);
325+
bool operator=(char&);
326+
bool operator=(volatile unsigned int&);
327+
bool operator=(const volatile unsigned char&);
328+
bool operator=(int);
329+
bool operator=(Foo&&);
330+
};
331+
"""
332+
tu_with_copy_assignment_operators = get_tu(
333+
source_with_copy_assignment_operators, lang="cpp"
334+
)
335+
tu_without_copy_assignment_operators = get_tu(
336+
source_without_copy_assignment_operators, lang="cpp"
337+
)
338+
339+
copy_assignment_operators_cursors = get_cursors(
340+
tu_with_copy_assignment_operators, "operator="
341+
)
342+
non_copy_assignment_operators_cursors = get_cursors(
343+
tu_without_copy_assignment_operators, "operator="
344+
)
345+
346+
self.assertEqual(len(copy_assignment_operators_cursors), 10)
347+
self.assertTrue(len(non_copy_assignment_operators_cursors), 9)
348+
349+
self.assertTrue(
350+
all(
351+
[
352+
cursor.is_copy_assignment_operator_method()
353+
for cursor in copy_assignment_operators_cursors
354+
]
355+
)
356+
)
357+
358+
self.assertFalse(
359+
any(
360+
[
361+
cursor.is_copy_assignment_operator_method()
362+
for cursor in non_copy_assignment_operators_cursors
363+
]
364+
)
365+
)
366+
282367
def test_is_move_assignment_operator_method(self):
283368
"""Ensure Cursor.is_move_assignment_operator_method works."""
284369
source_with_move_assignment_operators = """
@@ -482,6 +567,41 @@ def test_is_scoped_enum(self):
482567
self.assertFalse(regular_enum.is_scoped_enum())
483568
self.assertTrue(scoped_enum.is_scoped_enum())
484569

570+
def test_get_definition(self):
571+
"""Ensure Cursor.get_definition works."""
572+
tu = get_tu(
573+
"""
574+
class A {
575+
constexpr static int f(){return 3;}
576+
};
577+
struct B {
578+
int b = A::f();
579+
};
580+
""",
581+
lang="cpp",
582+
)
583+
curs = get_cursors(tu, "f")
584+
self.assertEqual(len(curs), 4)
585+
self.assertEqual(curs[0].kind, CursorKind.CXX_METHOD)
586+
self.assertEqual(curs[1].get_definition(), curs[0])
587+
self.assertEqual(curs[2].get_definition(), curs[0])
588+
self.assertEqual(curs[3].get_definition(), curs[0])
589+
590+
def test_get_usr(self):
591+
"""Ensure Cursor.get_usr works."""
592+
tu = get_tu(
593+
"""
594+
int add(int, int);
595+
int add(int a, int b) { return a + b; }
596+
int add(float a, float b) { return a + b; }
597+
""",
598+
lang="cpp",
599+
)
600+
curs = get_cursors(tu, "add")
601+
self.assertEqual(len(curs), 3)
602+
self.assertEqual(curs[0].get_usr(), curs[1].get_usr())
603+
self.assertNotEqual(curs[0].get_usr(), curs[2].get_usr())
604+
485605
def test_underlying_type(self):
486606
tu = get_tu("typedef int foo;")
487607
typedef = get_cursor(tu, "foo")
@@ -570,6 +690,23 @@ def test_enum_values_cpp(self):
570690
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
571691
self.assertEqual(ham.enum_value, 0x10000000000)
572692

693+
def test_enum_values_unsigned(self):
694+
tu = get_tu("enum TEST : unsigned char { SPAM=0, HAM = 200};", lang="cpp")
695+
enum = get_cursor(tu, "TEST")
696+
self.assertIsNotNone(enum)
697+
698+
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
699+
700+
enum_constants = list(enum.get_children())
701+
self.assertEqual(len(enum_constants), 2)
702+
703+
spam, ham = enum_constants
704+
705+
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
706+
self.assertEqual(spam.enum_value, 0)
707+
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
708+
self.assertEqual(ham.enum_value, 200)
709+
573710
def test_annotation_attribute(self):
574711
tu = get_tu(
575712
'int foo (void) __attribute__ ((annotate("here be annotation attribute")));'
@@ -625,6 +762,25 @@ def test_result_type_objc_method_decl(self):
625762
self.assertEqual(cursor.kind, CursorKind.OBJC_INSTANCE_METHOD_DECL)
626763
self.assertEqual(result_type.kind, TypeKind.VOID)
627764

765+
def test_storage_class(self):
766+
tu = get_tu(
767+
"""
768+
extern int ex;
769+
register int reg;
770+
int count(int a, int b){
771+
static int counter = 0;
772+
return 0;
773+
}
774+
""",
775+
lang="cpp",
776+
)
777+
cursor = get_cursor(tu, "ex")
778+
self.assertEqual(cursor.storage_class, StorageClass.EXTERN)
779+
cursor = get_cursor(tu, "counter")
780+
self.assertEqual(cursor.storage_class, StorageClass.STATIC)
781+
cursor = get_cursor(tu, "reg")
782+
self.assertEqual(cursor.storage_class, StorageClass.REGISTER)
783+
628784
def test_availability(self):
629785
tu = get_tu("class A { A(A const&) = delete; };", lang="cpp")
630786

@@ -681,6 +837,23 @@ def test_get_token_cursor(self):
681837
r_cursor = t_cursor.referenced # should not raise an exception
682838
self.assertEqual(r_cursor.kind, CursorKind.CLASS_DECL)
683839

840+
def test_get_field_offsetof(self):
841+
tu = get_tu(
842+
"struct myStruct {int a; char b; char c; short d; char e;};", lang="cpp"
843+
)
844+
c1 = get_cursor(tu, "myStruct")
845+
c2 = get_cursor(tu, "a")
846+
c3 = get_cursor(tu, "b")
847+
c4 = get_cursor(tu, "c")
848+
c5 = get_cursor(tu, "d")
849+
c6 = get_cursor(tu, "e")
850+
self.assertEqual(c1.get_field_offsetof(), -1)
851+
self.assertEqual(c2.get_field_offsetof(), 0)
852+
self.assertEqual(c3.get_field_offsetof(), 32)
853+
self.assertEqual(c4.get_field_offsetof(), 40)
854+
self.assertEqual(c5.get_field_offsetof(), 48)
855+
self.assertEqual(c6.get_field_offsetof(), 64)
856+
684857
def test_get_arguments(self):
685858
tu = get_tu("void foo(int i, int j);")
686859
foo = get_cursor(tu, "foo")
@@ -799,3 +972,13 @@ def test_binop(self):
799972
for op, typ in operators.items():
800973
c = get_cursor(tu, op)
801974
assert c.binary_operator == typ
975+
976+
def test_from_result_null(self):
977+
tu = get_tu("int a = 1+2;", lang="cpp")
978+
op = next(next(tu.cursor.get_children()).get_children())
979+
self.assertEqual(op.kind, CursorKind.BINARY_OPERATOR)
980+
self.assertEqual(op.get_definition(), None)
981+
982+
def test_from_cursor_result_null(self):
983+
tu = get_tu("")
984+
self.assertEqual(tu.cursor.semantic_parent, None)

clang/bindings/python/tests/cindex/test_diagnostics.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def test_diagnostic_fixit(self):
4646
self.assertEqual(tu.diagnostics[0].location.column, 26)
4747
self.assertRegex(tu.diagnostics[0].spelling, "use of GNU old-style.*")
4848
self.assertEqual(len(tu.diagnostics[0].fixits), 1)
49+
with self.assertRaises(IndexError):
50+
tu.diagnostics[0].fixits[1]
4951
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.line, 1)
5052
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.column, 26)
5153
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.line, 1)
@@ -97,6 +99,8 @@ def test_diagnostic_children(self):
9799

98100
children = d.children
99101
self.assertEqual(len(children), 1)
102+
with self.assertRaises(IndexError):
103+
children[1]
100104
self.assertEqual(children[0].severity, Diagnostic.Note)
101105
self.assertRegex(children[0].spelling, ".*declared here")
102106
self.assertEqual(children[0].location.line, 1)
@@ -111,3 +115,16 @@ def test_diagnostic_string_repr(self):
111115
repr(d),
112116
"<Diagnostic severity 3, location <SourceLocation file 't.c', line 1, column 26>, spelling \"expected ';' after struct\">",
113117
)
118+
119+
def test_diagnostic_string_format(self):
120+
tu = get_tu("struct MissingSemicolon{}")
121+
self.assertEqual(len(tu.diagnostics), 1)
122+
d = tu.diagnostics[0]
123+
124+
self.assertEqual(str(d), "t.c:1:26: error: expected ';' after struct")
125+
self.assertEqual(
126+
d.format(0b111111),
127+
"t.c:1:26: error: expected ';' after struct [3, Parse Issue]",
128+
)
129+
with self.assertRaises(ValueError):
130+
d.format(0b1000000)

clang/bindings/python/tests/cindex/test_type.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from clang.cindex import CursorKind
1111
from clang.cindex import TranslationUnit
1212
from clang.cindex import TypeKind
13+
from clang.cindex import RefQualifierKind
1314
from .util import get_cursor
15+
from .util import get_cursors
1416
from .util import get_tu
1517

1618

@@ -308,10 +310,10 @@ def test_element_type(self):
308310
def test_invalid_element_type(self):
309311
"""Ensure Type.element_type raises if type doesn't have elements."""
310312
tu = get_tu("int i;")
311-
i = get_cursor(tu, "i")
312-
self.assertIsNotNone(i)
313-
with self.assertRaises(Exception):
314-
i.element_type
313+
ty = get_cursor(tu, "i").type
314+
with self.assertRaises(Exception) as ctx:
315+
ty.element_type
316+
self.assertEqual(str(ctx.exception), "Element type not available on this type.")
315317

316318
def test_element_count(self):
317319
"""Ensure Type.element_count works."""
@@ -357,6 +359,49 @@ def test_is_restrict_qualified(self):
357359
self.assertTrue(i.type.is_restrict_qualified())
358360
self.assertFalse(j.type.is_restrict_qualified())
359361

362+
def test_get_result(self):
363+
tu = get_tu("void foo(); int bar(char, short);")
364+
foo = get_cursor(tu, "foo")
365+
bar = get_cursor(tu, "bar")
366+
self.assertEqual(foo.type.get_result().spelling, "void")
367+
self.assertEqual(bar.type.get_result().spelling, "int")
368+
369+
def test_get_class_type(self):
370+
tu = get_tu(
371+
"""
372+
class myClass
373+
{
374+
char *myAttr;
375+
};
376+
377+
char *myClass::*pMyAttr = &myClass::myAttr;
378+
""",
379+
lang="cpp",
380+
)
381+
cur = get_cursor(tu, "pMyAttr")
382+
self.assertEqual(cur.type.get_class_type().spelling, "myClass")
383+
384+
def test_get_named_type(self):
385+
tu = get_tu("using char_alias = char; char_alias xyz;", lang="cpp")
386+
cur = get_cursor(tu, "xyz")
387+
self.assertEqual(cur.type.get_named_type().spelling, "char_alias")
388+
389+
def test_get_ref_qualifier(self):
390+
tu = get_tu(
391+
"""
392+
class A
393+
{
394+
const int& getAttr() const &;
395+
int getAttr() const &&;
396+
};
397+
""",
398+
lang="cpp",
399+
)
400+
getters = get_cursors(tu, "getAttr")
401+
self.assertEqual(len(getters), 2)
402+
self.assertEqual(getters[0].type.get_ref_qualifier(), RefQualifierKind.LVALUE)
403+
self.assertEqual(getters[1].type.get_ref_qualifier(), RefQualifierKind.RVALUE)
404+
360405
def test_record_layout(self):
361406
"""Ensure Cursor.type.get_size, Cursor.type.get_align and
362407
Cursor.type.get_offset works."""

0 commit comments

Comments
 (0)