mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Move swift/codegen to misc/codegen
This commit is contained in:
686
misc/codegen/test/test_schemaloader.py
Normal file
686
misc/codegen/test/test_schemaloader.py
Normal file
@@ -0,0 +1,686 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from misc.codegen.test.utils import *
|
||||
from misc.codegen.lib import schemadefs as defs
|
||||
from misc.codegen.loaders.schemaloader import load
|
||||
|
||||
|
||||
def test_empty_schema():
|
||||
@load
|
||||
class data:
|
||||
pass
|
||||
|
||||
assert data.classes == {}
|
||||
assert data.includes == set()
|
||||
assert data.null is None
|
||||
assert data.null_class is None
|
||||
|
||||
|
||||
def test_one_empty_class():
|
||||
@load
|
||||
class data:
|
||||
class MyClass:
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'MyClass': schema.Class('MyClass'),
|
||||
}
|
||||
assert data.root_class is data.classes['MyClass']
|
||||
|
||||
|
||||
def test_two_empty_classes():
|
||||
@load
|
||||
class data:
|
||||
class MyClass1:
|
||||
pass
|
||||
|
||||
class MyClass2(MyClass1):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'MyClass1': schema.Class('MyClass1', derived={'MyClass2'}),
|
||||
'MyClass2': schema.Class('MyClass2', bases=['MyClass1']),
|
||||
}
|
||||
assert data.root_class is data.classes['MyClass1']
|
||||
|
||||
|
||||
def test_no_external_bases():
|
||||
class A:
|
||||
pass
|
||||
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class MyClass(A):
|
||||
pass
|
||||
|
||||
|
||||
def test_no_multiple_roots():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class MyClass1:
|
||||
pass
|
||||
|
||||
class MyClass2:
|
||||
pass
|
||||
|
||||
|
||||
def test_empty_classes_diamond():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
class C(A):
|
||||
pass
|
||||
|
||||
class D(B, C):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B', 'C'}),
|
||||
'B': schema.Class('B', bases=['A'], derived={'D'}),
|
||||
'C': schema.Class('C', bases=['A'], derived={'D'}),
|
||||
'D': schema.Class('D', bases=['B', 'C']),
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
def test_group():
|
||||
@load
|
||||
class data:
|
||||
@defs.group("xxx")
|
||||
class A:
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', group="xxx"),
|
||||
}
|
||||
|
||||
|
||||
def test_group_is_inherited():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
@defs.group('xxx')
|
||||
class C(A):
|
||||
pass
|
||||
|
||||
class D(B, C):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B', 'C'}),
|
||||
'B': schema.Class('B', bases=['A'], derived={'D'}),
|
||||
'C': schema.Class('C', bases=['A'], derived={'D'}, group='xxx'),
|
||||
'D': schema.Class('D', bases=['B', 'C'], group='xxx'),
|
||||
}
|
||||
|
||||
|
||||
def test_no_mixed_groups_in_bases():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
@defs.group('x')
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
@defs.group('y')
|
||||
class C(A):
|
||||
pass
|
||||
|
||||
class D(B, C):
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
|
||||
|
||||
def test_lowercase_rejected():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class aLowerCase:
|
||||
pass
|
||||
|
||||
|
||||
def test_properties():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
one: defs.string
|
||||
two: defs.optional[defs.int]
|
||||
three: defs.list[defs.boolean]
|
||||
four: defs.list[defs.optional[defs.string]]
|
||||
five: defs.predicate
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('one', 'string'),
|
||||
schema.OptionalProperty('two', 'int'),
|
||||
schema.RepeatedProperty('three', 'boolean'),
|
||||
schema.RepeatedOptionalProperty('four', 'string'),
|
||||
schema.PredicateProperty('five'),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
def test_class_properties():
|
||||
class A:
|
||||
pass
|
||||
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
one: A
|
||||
two: defs.optional[A]
|
||||
three: defs.list[A]
|
||||
four: defs.list[defs.optional[A]]
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}),
|
||||
'B': schema.Class('B', bases=['A'], properties=[
|
||||
schema.SingleProperty('one', 'A'),
|
||||
schema.OptionalProperty('two', 'A'),
|
||||
schema.RepeatedProperty('three', 'A'),
|
||||
schema.RepeatedOptionalProperty('four', 'A'),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
def test_string_reference_class_properties():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
one: "A"
|
||||
two: defs.optional["A"]
|
||||
three: defs.list["A"]
|
||||
four: defs.list[defs.optional["A"]]
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('one', 'A'),
|
||||
schema.OptionalProperty('two', 'A'),
|
||||
schema.RepeatedProperty('three', 'A'),
|
||||
schema.RepeatedOptionalProperty('four', 'A'),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("spec", [lambda t: t, lambda t: defs.optional[t], lambda t: defs.list[t],
|
||||
lambda t: defs.list[defs.optional[t]]])
|
||||
def test_string_reference_dangling(spec):
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: spec("B")
|
||||
|
||||
|
||||
def test_children():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
one: "A" | defs.child
|
||||
two: defs.optional["A"] | defs.child
|
||||
three: defs.list["A"] | defs.child
|
||||
four: defs.list[defs.optional["A"]] | defs.child
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('one', 'A', is_child=True),
|
||||
schema.OptionalProperty('two', 'A', is_child=True),
|
||||
schema.RepeatedProperty('three', 'A', is_child=True),
|
||||
schema.RepeatedOptionalProperty('four', 'A', is_child=True),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("spec", [defs.string, defs.int, defs.boolean, defs.predicate])
|
||||
def test_builtin_and_predicate_children_not_allowed(spec):
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: spec | defs.child
|
||||
|
||||
|
||||
_pragmas = [(defs.qltest.skip, "qltest_skip"),
|
||||
(defs.qltest.collapse_hierarchy, "qltest_collapse_hierarchy"),
|
||||
(defs.qltest.uncollapse_hierarchy, "qltest_uncollapse_hierarchy"),
|
||||
(defs.cpp.skip, "cpp_skip"),
|
||||
(defs.ql.internal, "ql_internal"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pragma,expected", _pragmas)
|
||||
def test_property_with_pragma(pragma, expected):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: defs.string | pragma
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'string', pragmas=[expected]),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
def test_property_with_pragmas():
|
||||
spec = defs.string
|
||||
for pragma, _ in _pragmas:
|
||||
spec |= pragma
|
||||
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: spec
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'string', pragmas=[expected for _, expected in _pragmas]),
|
||||
]),
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pragma,expected", _pragmas)
|
||||
def test_class_with_pragma(pragma, expected):
|
||||
@load
|
||||
class data:
|
||||
@pragma
|
||||
class A:
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', pragmas=[expected]),
|
||||
}
|
||||
|
||||
|
||||
def test_class_with_pragmas():
|
||||
def apply_pragmas(cls):
|
||||
for p, _ in _pragmas:
|
||||
p(cls)
|
||||
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
apply_pragmas(A)
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', pragmas=[e for _, e in _pragmas]),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
@defs.synth.from_class(A)
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(from_class="A")),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class_ref():
|
||||
@load
|
||||
class data:
|
||||
@defs.synth.from_class("B")
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=schema.IpaInfo(from_class="B")),
|
||||
'B': schema.Class('B', bases=['A']),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_from_class_dangling():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
@defs.synth.from_class("X")
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
def test_ipa_class_on():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
pass
|
||||
|
||||
@defs.synth.on_arguments(a=A, i=defs.int)
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=True),
|
||||
'B': schema.Class('B', bases=['A'], ipa=schema.IpaInfo(on_arguments={'a': 'A', 'i': 'int'})),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_class_on_ref():
|
||||
class A:
|
||||
pass
|
||||
|
||||
@load
|
||||
class data:
|
||||
@defs.synth.on_arguments(b="B", i=defs.int)
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', derived={'B'}, ipa=schema.IpaInfo(on_arguments={'b': 'B', 'i': 'int'})),
|
||||
'B': schema.Class('B', bases=['A']),
|
||||
}
|
||||
|
||||
|
||||
def test_ipa_class_on_dangling():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
@defs.synth.on_arguments(s=defs.string, a="A", i=defs.int)
|
||||
class B:
|
||||
pass
|
||||
|
||||
|
||||
def test_ipa_class_hierarchy():
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
class Base(Root):
|
||||
pass
|
||||
|
||||
class Intermediate(Base):
|
||||
pass
|
||||
|
||||
@defs.synth.on_arguments(a=Base, i=defs.int)
|
||||
class A(Intermediate):
|
||||
pass
|
||||
|
||||
@defs.synth.from_class(Base)
|
||||
class B(Base):
|
||||
pass
|
||||
|
||||
class C(Root):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'Root': schema.Class('Root', derived={'Base', 'C'}),
|
||||
'Base': schema.Class('Base', bases=['Root'], derived={'Intermediate', 'B'}, ipa=True),
|
||||
'Intermediate': schema.Class('Intermediate', bases=['Base'], derived={'A'}, ipa=True),
|
||||
'A': schema.Class('A', bases=['Intermediate'], ipa=schema.IpaInfo(on_arguments={'a': 'Base', 'i': 'int'})),
|
||||
'B': schema.Class('B', bases=['Base'], ipa=schema.IpaInfo(from_class='Base')),
|
||||
'C': schema.Class('C', bases=['Root']),
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important class."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("very important property.")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[schema.SingleProperty('x', 'int', description=["very important property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_newline():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important
|
||||
class."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important", "class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_newline():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""very important
|
||||
property.""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A',
|
||||
properties=[schema.SingleProperty('x', 'int', description=["very important", "property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_stripped():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
"""
|
||||
|
||||
Very important class.
|
||||
|
||||
"""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_stripped():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""
|
||||
|
||||
very important property.
|
||||
|
||||
""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[schema.SingleProperty('x', 'int', description=["very important property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_split():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important class.
|
||||
|
||||
As said, very important."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class.", "", "As said, very important."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_split():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""very important property.
|
||||
|
||||
Very very important.""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'int', description=["very important property.", "", "Very very important."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_indent():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
"""
|
||||
Very important class.
|
||||
As said, very important.
|
||||
"""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class.", " As said, very important."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_indent():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""
|
||||
very important property.
|
||||
Very very important.
|
||||
""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'int', description=["very important property.", " Very very important."])])
|
||||
}
|
||||
|
||||
|
||||
def test_property_doc_override():
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.doc("y")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'int', doc="y")]),
|
||||
}
|
||||
|
||||
|
||||
def test_property_doc_override_no_newlines():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.doc("no multiple\nlines")
|
||||
|
||||
|
||||
def test_property_doc_override_no_trailing_dot():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.doc("no dots please.")
|
||||
|
||||
|
||||
def test_class_default_doc_name():
|
||||
@load
|
||||
class data:
|
||||
@defs.ql.default_doc_name("b")
|
||||
class A:
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', default_doc_name="b"),
|
||||
}
|
||||
|
||||
|
||||
def test_null_class():
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
@defs.use_for_null
|
||||
class Null(Root):
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
'Root': schema.Class('Root', derived={'Null'}),
|
||||
'Null': schema.Class('Null', bases=['Root']),
|
||||
}
|
||||
assert data.null == 'Null'
|
||||
assert data.null_class is data.classes[data.null]
|
||||
|
||||
|
||||
def test_null_class_cannot_be_derived():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
@defs.use_for_null
|
||||
class Null(Root):
|
||||
pass
|
||||
|
||||
class Impossible(Null):
|
||||
pass
|
||||
|
||||
|
||||
def test_null_class_cannot_be_defined_multiple_times():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
@defs.use_for_null
|
||||
class Null1(Root):
|
||||
pass
|
||||
|
||||
@defs.use_for_null
|
||||
class Null2(Root):
|
||||
pass
|
||||
|
||||
|
||||
def test_uppercase_acronyms_are_rejected():
|
||||
with pytest.raises(schema.Error):
|
||||
@load
|
||||
class data:
|
||||
class Root:
|
||||
pass
|
||||
|
||||
class ROTFLNode(Root):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
Reference in New Issue
Block a user