mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
This switched `codegen` from the `autopep8` formatting to the `black` one, and applies it to `bulk_mad_generator.py` as well. We can enroll more python scripts to it in the future.
1185 lines
26 KiB
Python
1185 lines
26 KiB
Python
import sys
|
|
|
|
import pytest
|
|
from misc.codegen.lib.schemadefs import optional
|
|
|
|
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", pragmas={"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"}, pragmas={"group": "xxx"}),
|
|
"D": schema.Class("D", bases=["B", "C"], pragmas={"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
|
|
six: defs.set[defs.string]
|
|
|
|
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"),
|
|
schema.RepeatedUnorderedProperty("six", "string"),
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
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]]
|
|
five: defs.set[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"),
|
|
schema.RepeatedUnorderedProperty("five", "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"]]
|
|
five: defs.set["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"),
|
|
schema.RepeatedUnorderedProperty("five", "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, defs.set["A"]]
|
|
)
|
|
def test_builtin_predicate_and_set_children_not_allowed(spec):
|
|
with pytest.raises(schema.Error):
|
|
|
|
@load
|
|
class data:
|
|
class A:
|
|
x: spec | defs.child
|
|
|
|
|
|
_class_pragmas = [
|
|
(defs.qltest.collapse_hierarchy, "qltest_collapse_hierarchy"),
|
|
(defs.qltest.uncollapse_hierarchy, "qltest_uncollapse_hierarchy"),
|
|
(defs.qltest.skip, "qltest_skip"),
|
|
]
|
|
|
|
_property_pragmas = [
|
|
(defs.cpp.skip, "cpp_skip"),
|
|
(defs.ql.internal, "ql_internal"),
|
|
]
|
|
|
|
_pragmas = _class_pragmas + _property_pragmas
|
|
|
|
|
|
@pytest.mark.parametrize("pragma,expected", _property_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 _property_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 _property_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_synth_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"}, pragmas={"synth": True}),
|
|
"B": schema.Class(
|
|
"B", bases=["A"], pragmas={"synth": schema.SynthInfo(from_class="A")}
|
|
),
|
|
}
|
|
|
|
|
|
def test_synth_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"}, pragmas={"synth": schema.SynthInfo(from_class="B")}
|
|
),
|
|
"B": schema.Class("B", bases=["A"]),
|
|
}
|
|
|
|
|
|
def test_synth_from_class_dangling():
|
|
with pytest.raises(schema.Error):
|
|
|
|
@load
|
|
class data:
|
|
@defs.synth.from_class("X")
|
|
class A:
|
|
pass
|
|
|
|
|
|
def test_synth_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"}, pragmas={"synth": True}),
|
|
"B": schema.Class(
|
|
"B",
|
|
bases=["A"],
|
|
pragmas={"synth": schema.SynthInfo(on_arguments={"a": "A", "i": "int"})},
|
|
),
|
|
}
|
|
|
|
|
|
def test_synth_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"},
|
|
pragmas={"synth": schema.SynthInfo(on_arguments={"b": "B", "i": "int"})},
|
|
),
|
|
"B": schema.Class("B", bases=["A"]),
|
|
}
|
|
|
|
|
|
def test_synth_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_synth_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"},
|
|
pragmas={"synth": True},
|
|
),
|
|
"Intermediate": schema.Class(
|
|
"Intermediate", bases=["Base"], derived={"A"}, pragmas={"synth": True}
|
|
),
|
|
"A": schema.Class(
|
|
"A",
|
|
bases=["Intermediate"],
|
|
pragmas={"synth": schema.SynthInfo(on_arguments={"a": "Base", "i": "int"})},
|
|
),
|
|
"B": schema.Class(
|
|
"B", bases=["Base"], pragmas={"synth": schema.SynthInfo(from_class="Base")}
|
|
),
|
|
"C": schema.Class("C", bases=["Root"]),
|
|
}
|
|
|
|
|
|
def test_synthesized_property():
|
|
@load
|
|
class data:
|
|
class A:
|
|
x: defs.int | defs.synth
|
|
|
|
assert data.classes["A"].properties == [
|
|
schema.SingleProperty("x", "int", synth=True)
|
|
]
|
|
|
|
|
|
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", pragmas={"ql_default_doc_name": "b"}),
|
|
}
|
|
|
|
|
|
def test_db_table_name():
|
|
@load
|
|
class data:
|
|
class A:
|
|
x: optional[int] | defs.ql.db_table_name("foo")
|
|
|
|
assert data.classes == {
|
|
"A": schema.Class(
|
|
"A",
|
|
properties=[
|
|
schema.OptionalProperty("x", "int", pragmas={"ql_db_table_name": "foo"})
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
def test_hideable():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
@defs.ql.hideable
|
|
class A(Root):
|
|
pass
|
|
|
|
class IndirectlyHideable(Root):
|
|
pass
|
|
|
|
class B(A, IndirectlyHideable):
|
|
pass
|
|
|
|
class NonHideable(Root):
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class(
|
|
"Root",
|
|
derived={"A", "IndirectlyHideable", "NonHideable"},
|
|
pragmas=["ql_hideable"],
|
|
),
|
|
"A": schema.Class("A", bases=["Root"], derived={"B"}, pragmas=["ql_hideable"]),
|
|
"IndirectlyHideable": schema.Class(
|
|
"IndirectlyHideable", bases=["Root"], derived={"B"}, pragmas=["ql_hideable"]
|
|
),
|
|
"B": schema.Class(
|
|
"B", bases=["A", "IndirectlyHideable"], pragmas=["ql_hideable"]
|
|
),
|
|
"NonHideable": schema.Class("NonHideable", bases=["Root"]),
|
|
}
|
|
|
|
|
|
def test_test_with():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
class A(Root):
|
|
pass
|
|
|
|
@defs.qltest.test_with(A)
|
|
class B(Root):
|
|
pass
|
|
|
|
@defs.qltest.test_with("D")
|
|
class C(Root):
|
|
pass
|
|
|
|
class D(Root):
|
|
pass
|
|
|
|
class E(B):
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class("Root", derived=set("ABCD")),
|
|
"A": schema.Class("A", bases=["Root"]),
|
|
"B": schema.Class(
|
|
"B", bases=["Root"], pragmas={"qltest_test_with": "A"}, derived={"E"}
|
|
),
|
|
"C": schema.Class("C", bases=["Root"], pragmas={"qltest_test_with": "D"}),
|
|
"D": schema.Class("D", bases=["Root"]),
|
|
"E": schema.Class("E", bases=["B"], pragmas={"qltest_test_with": "A"}),
|
|
}
|
|
|
|
|
|
def test_annotate_docstring():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
"""old docstring"""
|
|
|
|
class A(Root):
|
|
"""A docstring"""
|
|
|
|
@defs.annotate(Root)
|
|
class _:
|
|
"""
|
|
new
|
|
docstring
|
|
"""
|
|
|
|
@defs.annotate(A)
|
|
class _:
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class("Root", doc=["new", "docstring"], derived={"A"}),
|
|
"A": schema.Class("A", bases=["Root"], doc=["A docstring"]),
|
|
}
|
|
|
|
|
|
def test_annotate_decorations():
|
|
@load
|
|
class data:
|
|
@defs.qltest.skip
|
|
class Root:
|
|
pass
|
|
|
|
@defs.annotate(Root)
|
|
@defs.qltest.collapse_hierarchy
|
|
@defs.ql.hideable
|
|
@defs.cpp.skip
|
|
class _:
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class(
|
|
"Root",
|
|
pragmas=[
|
|
"qltest_skip",
|
|
"cpp_skip",
|
|
"ql_hideable",
|
|
"qltest_collapse_hierarchy",
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
def test_annotate_fields():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
x: defs.int
|
|
y: defs.optional["Root"] | defs.child
|
|
|
|
@defs.annotate(Root)
|
|
class _:
|
|
x: defs._ | defs.doc("foo")
|
|
y: defs._ | defs.ql.internal
|
|
z: defs.string
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class(
|
|
"Root",
|
|
properties=[
|
|
schema.SingleProperty("x", "int", doc="foo"),
|
|
schema.OptionalProperty(
|
|
"y", "Root", pragmas=["ql_internal"], is_child=True
|
|
),
|
|
schema.SingleProperty("z", "string"),
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
def test_annotate_fields_negations():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
x: defs.int | defs.ql.internal
|
|
y: defs.optional["Root"] | defs.child | defs.desc("foo\nbar\n")
|
|
z: defs.string | defs.synth | defs.doc("foo")
|
|
|
|
@defs.annotate(Root)
|
|
class _:
|
|
x: defs._ | ~defs.ql.internal
|
|
y: defs._ | ~defs.child | ~defs.ql.internal | ~defs.desc
|
|
z: defs._ | ~defs.synth | ~defs.doc
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class(
|
|
"Root",
|
|
properties=[
|
|
schema.SingleProperty("x", "int"),
|
|
schema.OptionalProperty("y", "Root"),
|
|
schema.SingleProperty("z", "string"),
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
def test_annotate_non_existing_field():
|
|
with pytest.raises(schema.Error):
|
|
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
@defs.annotate(Root)
|
|
class _:
|
|
x: defs._ | defs.doc("foo")
|
|
|
|
|
|
def test_annotate_not_underscore():
|
|
with pytest.raises(schema.Error):
|
|
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
@defs.annotate(Root)
|
|
class Something:
|
|
"""
|
|
new
|
|
docstring
|
|
"""
|
|
|
|
|
|
def test_annotate_replace_bases():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
class A(Root):
|
|
pass
|
|
|
|
class B(Root):
|
|
pass
|
|
|
|
class C(B):
|
|
pass
|
|
|
|
class Derived(A, B):
|
|
pass
|
|
|
|
@defs.annotate(Derived, replace_bases={B: C})
|
|
class _:
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class("Root", derived={"A", "B"}),
|
|
"A": schema.Class("A", bases=["Root"], derived={"Derived"}),
|
|
"B": schema.Class("B", bases=["Root"], derived={"C"}),
|
|
"C": schema.Class("C", bases=["B"], derived={"Derived"}),
|
|
"Derived": schema.Class("Derived", bases=["A", "C"]),
|
|
}
|
|
|
|
|
|
def test_annotate_add_bases():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
class A(Root):
|
|
pass
|
|
|
|
class B(Root):
|
|
pass
|
|
|
|
class C(Root):
|
|
pass
|
|
|
|
class Derived(A):
|
|
pass
|
|
|
|
@defs.annotate(Derived, add_bases=(B, C))
|
|
class _:
|
|
pass
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class("Root", derived={"A", "B", "C"}),
|
|
"A": schema.Class("A", bases=["Root"], derived={"Derived"}),
|
|
"B": schema.Class("B", bases=["Root"], derived={"Derived"}),
|
|
"C": schema.Class("C", bases=["Root"], derived={"Derived"}),
|
|
"Derived": schema.Class("Derived", bases=["A", "B", "C"]),
|
|
}
|
|
|
|
|
|
def test_annotate_drop_field():
|
|
@load
|
|
class data:
|
|
class Root:
|
|
x: defs.int
|
|
y: defs.string
|
|
z: defs.boolean
|
|
|
|
@defs.annotate(Root)
|
|
class _:
|
|
y: defs.drop
|
|
|
|
assert data.classes == {
|
|
"Root": schema.Class(
|
|
"Root",
|
|
properties=[
|
|
schema.SingleProperty("x", "int"),
|
|
schema.SingleProperty("z", "boolean"),
|
|
],
|
|
),
|
|
}
|
|
|
|
|
|
def test_test_with_unknown_string():
|
|
with pytest.raises(schema.Error):
|
|
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
@defs.qltest.test_with("B")
|
|
class A(Root):
|
|
pass
|
|
|
|
|
|
def test_test_with_unknown_class():
|
|
with pytest.raises(schema.Error):
|
|
|
|
class B:
|
|
pass
|
|
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
@defs.qltest.test_with(B)
|
|
class A(Root):
|
|
pass
|
|
|
|
|
|
def test_test_with_double():
|
|
with pytest.raises(schema.Error):
|
|
|
|
class B:
|
|
pass
|
|
|
|
@load
|
|
class data:
|
|
class Root:
|
|
pass
|
|
|
|
class A(Root):
|
|
pass
|
|
|
|
@defs.qltest.test_with("C")
|
|
class B(Root):
|
|
pass
|
|
|
|
@defs.qltest.test_with(A)
|
|
class C(Root):
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|