def test_write_specification_files_missing_deps(tmp_path: Path) -> None: s = ModularInteger("P::S", Number(65536)) t = ModularInteger("P::T", Number(256)) v = mty.Sequence("P::V", element_type=t) m = Message("P::M", [Link(INITIAL, Field("Foo")), Link(Field("Foo"), FINAL)], {Field("Foo"): t}) Model([s, v, m]).write_specification_files(tmp_path) expected_path = tmp_path / Path("p.rflx") assert list(tmp_path.glob("*.rflx")) == [expected_path] assert expected_path.read_text() == textwrap.dedent( """\ package P is type S is mod 65536; type T is mod 256; type V is sequence of P::T; type M is message Foo : P::T; end message; end P;""" )
def test_incongruent_overlay() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2")), Link(Field("F2"), Field("F3"), first=First("F1")), Link(Field("F3"), Field("F4")), Link(Field("F4"), FINAL), ] u8 = ModularInteger("P.U8", Number(256)) u16 = ModularInteger("P.U16", Number(65536)) types = { Field("F1"): u8, Field("F2"): u8, Field("F3"): u16, Field("F4"): u16, } assert_message_model_error( structure, types, r"^" r'model: error: field "F3" not congruent with overlaid field "F1"\n' r'model: info: unsatisfied "F1\'First = Message\'First"\n' r'model: info: unsatisfied "F1\'Last = [(][(]Message\'First [+] 8[)][)] - 1"\n' r'model: info: unsatisfied "[(][(]F1\'First [+] 16[)][)] - 1 = F1\'Last"' r"$", )
def create_array_message() -> Message: length_type = ModularInteger("Arrays.Length", Pow(Number(2), Number(8))) modular_type = ModularInteger("Arrays.Modular_Integer", Pow(Number(2), Number(16))) modular_vector_type = Array("Arrays.Modular_Vector", modular_type) range_type = RangeInteger("Arrays.Range_Integer", Number(1), Number(100), Number(8)) range_vector_type = Array("Arrays.Range_Vector", range_type) enum_type = Enumeration( "Arrays.Enumeration", { "ZERO": Number(0), "ONE": Number(1), "TWO": Number(2) }, Number(8), False, ) enum_vector_type = Array("Arrays.Enumeration_Vector", enum_type) av_enum_type = Enumeration( "Arrays.AV_Enumeration", { "AV_ZERO": Number(0), "AV_ONE": Number(1), "AV_TWO": Number(2) }, Number(8), True, ) av_enum_vector_type = Array("Arrays.AV_Enumeration_Vector", av_enum_type) structure = [ Link(INITIAL, Field("Length")), Link(Field("Length"), Field("Modular_Vector"), length=Mul(Variable("Length"), Number(8))), Link(Field("Modular_Vector"), Field("Range_Vector"), length=Number(16)), Link(Field("Range_Vector"), Field("Enumeration_Vector"), length=Number(16)), Link(Field("Enumeration_Vector"), Field("AV_Enumeration_Vector"), length=Number(16)), Link(Field("AV_Enumeration_Vector"), FINAL), ] types = { Field("Length"): length_type, Field("Modular_Vector"): modular_vector_type, Field("Range_Vector"): range_vector_type, Field("Enumeration_Vector"): enum_vector_type, Field("AV_Enumeration_Vector"): av_enum_vector_type, } return Message("Arrays.Message", structure, types)
def test_message_type_message() -> None: simple_structure = [ Link(INITIAL, Field("Bar")), Link(Field("Bar"), Field("Baz")), Link(Field("Baz"), FINAL), ] simple_types = { Field("Bar"): ModularInteger("Message_Type.T", Number(256)), Field("Baz"): ModularInteger("Message_Type.T", Number(256)), } simple_message = Message("Message_Type.Simple_PDU", simple_structure, simple_types) structure = [ Link(INITIAL, Field("Foo")), Link(Field("Foo"), Field("Bar"), LessEqual(Variable("Foo"), Number(30, 16))), Link(Field("Foo"), Field("Baz"), Greater(Variable("Foo"), Number(30, 16))), Link(Field("Bar"), Field("Baz")), Link(Field("Baz"), FINAL), ] types = { **simple_types, **{Field("Foo"): ModularInteger("Message_Type.T", Number(256))}, } message = Message("Message_Type.PDU", structure, types) empty_message = Message("Message_Type.Empty_PDU", [], {}) assert_messages_files( [f"{TESTDIR}/message_type.rflx"], [message, simple_message, empty_message] )
def test_type_name(self) -> None: t = ModularInteger("Package.Type_Name", Number(256)) self.assertEqual(t.name, "Type_Name") self.assertEqual(t.package, "Package") with self.assertRaises(ModelError): ModularInteger("X", Number(256)) with self.assertRaises(ModelError): ModularInteger("X.Y.Z", Number(256))
def test_array_assign_incorrect_values( tlv: MessageValue, frame: MessageValue, array_type_foo: MessageValue, enum_value: EnumValue ) -> None: # pylint: disable=protected-access type_array = ArrayValue(Array("Test.Array", ModularInteger("Test.Mod_Int", Number(256)))) msg_array = ArrayValue(Array("Test.MsgArray", tlv._type)) intval = IntegerValue(ModularInteger("Test.Int", Number(256))) enum_value.assign("One") with pytest.raises(ValueError, match="cannot assign EnumValue to an array of ModularInteger"): type_array.assign([enum_value]) tlv.set("Tag", "Msg_Data") with pytest.raises( ValueError, match='cannot assign message "Message" to array of messages: all messages must be valid', ): msg_array.assign([tlv]) with pytest.raises(ValueError, match="cannot assign EnumValue to an array of Message"): msg_array.assign([enum_value]) tlv.set("Tag", "Msg_Data") tlv.set("Length", 4) tlv.set("Value", b"\x00\x00\x00\x00") frame.set("Destination", 0) frame.set("Source", 0) frame.set("Type_Length_TPID", 47) frame.set("Type_Length", 1537) frame.set("Payload", bytes(46)) with pytest.raises(ValueError, match='cannot assign "Frame" to an array of "Message"'): msg_array.assign([tlv, frame]) with pytest.raises( ValueError, match="cannot parse nested messages in array of type TLV.Message: Error while setting " "value for field Tag: 'Number 0 is not a valid enum value'", ): msg_array.parse(Bitstring("0001111")) tlv.set("Tag", "Msg_Data") tlv._fields["Length"].typeval.assign(111111111111111, False) with pytest.raises( ValueError, match='cannot assign message "Message" to array of messages: all messages must be valid', ): msg_array.assign([tlv]) assert msg_array.value == [] intval.assign(5) array_type_foo.set("Length", 42) with pytest.raises( ValueError, match="invalid data length: input length is 8 while expected input length is 336", ): array_type_foo.set("Bytes", [intval])
def test_array_preserve_value(enum_value: EnumValue) -> None: intval = IntegerValue(ModularInteger("Test.Int", Number(256))) intval.assign(1) enum_value.assign("One") type_array = ArrayValue(Array("Test.Array", ModularInteger("Test.Mod_Int", Number(256)))) type_array.assign([intval]) assert type_array.value == [intval] with pytest.raises(ValueError, match="cannot assign EnumValue to an array of ModularInteger"): type_array.assign([enum_value]) assert type_array.value == [intval]
def test_array_nested_values(array_type_foo: MessageValue) -> None: a = IntegerValue(ModularInteger("Array_Type.Byte_One", Number(256))) b = IntegerValue(ModularInteger("Array_Type.Byte_Two", Number(256))) c = IntegerValue(ModularInteger("Array_Type.Byte_Three", Number(256))) a.assign(5) b.assign(6) c.assign(7) byte_array = [a, b, c] array_type_foo.set("Length", 3) array_type_foo.set("Bytes", byte_array) assert array_type_foo.valid_message assert array_type_foo.bytestring == b"\x03\x05\x06\x07"
def test_type_name() -> None: t = ModularInteger("Package.Type_Name", Number(256)) assert t.name == "Type_Name" assert t.package == ID("Package") assert_type_error( ModularInteger("X", Number(256), Location((10, 20))), r'^<stdin>:10:20: model: error: unexpected format of type name "X"$', ) assert_type_error( ModularInteger("X.Y.Z", Number(256), Location((10, 20))), '^<stdin>:10:20: model: error: unexpected format of type name "X.Y.Z"$', )
def test_write_specification_file_multiple_packages_missing_deps(tmp_path: Path) -> None: t = ModularInteger("P::T", Number(256)) u = mty.Sequence("R::U", element_type=t) u1 = mty.Sequence("Q::U1", element_type=t) v = ModularInteger("R::V", Number(65536)) links = [ Link(INITIAL, Field("Victor")), Link(Field("Victor"), Field("Uniform")), Link(Field("Uniform"), FINAL), ] fields = {Field("Victor"): v, Field("Uniform"): u} m = Message("R::M", links, fields) Model([u1, m, u, v]).write_specification_files(tmp_path) p_path, q_path, r_path = (tmp_path / Path(pkg + ".rflx") for pkg in ("p", "q", "r")) assert set(tmp_path.glob("*.rflx")) == {p_path, q_path, r_path} assert p_path.read_text() == textwrap.dedent( """\ package P is type T is mod 256; end P;""" ) assert q_path.read_text() == textwrap.dedent( """\ with P; package Q is type U1 is sequence of P::T; end Q;""" ) assert r_path.read_text() == textwrap.dedent( """\ with P; package R is type V is mod 65536; type U is sequence of P::T; type M is message Victor : R::V then Uniform with Size => Message'Last - Victor'Last; Uniform : R::U; end message; end R;""" )
def test_pdu_fields_invalid_dupe(self) -> None: t1 = ModularInteger('T1', Number(2)) t2 = ModularInteger('T2', Number(4)) initial = InitialNode() n1 = Node('X', t1) n2 = Node('X', t2) initial.edges = [Edge(n1, TRUE)] n1.edges = [Edge(n2, TRUE)] n2.edges = [Edge(FINAL, TRUE)] with self.assertRaises(ModelError): PDU('Z', initial).fields()
def test_array_type_spec() -> None: spec = { "Array_Type": Specification( ContextSpec([]), PackageSpec( "Array_Type", [ ModularInteger("__PACKAGE__.Byte", Number(256)), ArraySpec("__PACKAGE__.Bytes", ReferenceSpec("__PACKAGE__.Byte")), MessageSpec( "__PACKAGE__.Foo", [ Component( "Length", "Byte", [Then("Bytes", UNDEFINED, Mul(Variable("Length"), Number(8)))], ), Component("Bytes", "Bytes"), ], ), ArraySpec("__PACKAGE__.Bar", ReferenceSpec("__PACKAGE__.Foo")), ], ), ) } assert_specifications_files([f"{TESTDIR}/array_type.rflx"], spec)
def test_graph_object() -> None: f_type = ModularInteger("P::T", Pow(Number(2), Number(32))) m = Message( "P::M", structure=[Link(INITIAL, Field("X")), Link(Field("X"), FINAL)], types={Field("X"): f_type}, ) g = create_message_graph(m) assert [(e.get_source(), e.get_destination()) for e in g.get_edges()] == [ ("Initial", "intermediate_0"), ("intermediate_0", "X"), ("X", "intermediate_1"), ("intermediate_1", "Final"), ] assert [n.get_name() for n in g.get_nodes()] == [ "graph", "edge", "node", "Initial", "X", "intermediate_0", "intermediate_1", "Final", ]
def test_integer_type_spec() -> None: spec = { "Integer_Type": Specification( ContextSpec([]), PackageSpec( "Integer_Type", [ RangeInteger("__PACKAGE__.Page_Num", Number(1), Number(2000), Number(16)), RangeInteger("__PACKAGE__.Line_Size", Number(0), Number(255), Number(8)), ModularInteger("__PACKAGE__.Byte", Number(256)), ModularInteger("__PACKAGE__.Hash_Index", Number(64)), ], ), ) } assert_specifications_files([f"{TESTDIR}/integer_type.rflx"], spec)
def test_aggregate_equal_array_invalid_length() -> None: magic = Field(ID("Magic", Location((3, 5)))) structure = [ Link(INITIAL, magic, length=Number(40, location=Location((19, 17)))), Link( magic, FINAL, condition=NotEqual(Variable("Magic"), Aggregate(Number(1), Number(2)), Location((17, 3))), ), ] types = { Field("Magic"): Array( "P.Arr", ModularInteger("P.Modular", Number(128), location=Location((66, 3)))), } assert_message_model_error( structure, types, r"^" r'<stdin>:17:3: model: error: contradicting condition in "P.M"\n' r'<stdin>:3:5: model: info: on path: "Magic"\n' r'<stdin>:17:3: model: info: unsatisfied "2 [*] Modular\'Length = Magic\'Length"\n' r'<stdin>:66:3: model: info: unsatisfied "Modular\'Length = 7"\n' r'<stdin>:19:17: model: info: unsatisfied "Magic\'Length = 40"', )
def parse_type(string: str, location: int, tokens: ParseResults) -> Type: try: name = tokens[1] full_name = f"__PACKAGE__.{name}" if tokens[3] == "mod": return ModularInteger(full_name, *tokens[4:6]) if tokens[3] == "range": tokens[6] = tokens[6]["size"] return RangeInteger(full_name, *tokens[4:7]) if tokens[3] == "message": return MessageSpec(full_name, tokens[4]) if tokens[3] == "null message": return MessageSpec(full_name, []) if tokens[3] == "(": elements = dict(tokens[4:-2]) aspects = tokens[-1] if len(elements) < len(tokens[4:-2]): raise ModelError(f'"{name}" contains duplicate elements') if "always_valid" not in aspects: aspects["always_valid"] = False return Enumeration(full_name, elements, aspects["size"], aspects["always_valid"]) if tokens[3] == "new": return DerivationSpec(full_name, tokens[4]) if tokens[3] == "array of": return Array( full_name, Reference(tokens[4] if "." in tokens[4] else f"__PACKAGE__.{tokens[4]}")) except ModelError as e: raise ParseFatalException(string, location, e) raise ParseFatalException(string, location, "unexpected type")
def create_tlv_message() -> Message: tag_type = Enumeration("TLV.Tag", { "Msg_Data": Number(1), "Msg_Error": Number(3) }, Number(2), False) length_type = ModularInteger("TLV.Length", Pow(Number(2), Number(14))) structure = [ Link(INITIAL, Field("Tag")), Link(Field("Tag"), Field("Length"), Equal(Variable("Tag"), Variable("Msg_Data"))), Link(Field("Tag"), FINAL, Equal(Variable("Tag"), Variable("Msg_Error"))), Link(Field("Length"), Field("Value"), length=Mul(Variable("Length"), Number(8))), Link(Field("Value"), FINAL), ] types = { Field("Tag"): tag_type, Field("Length"): length_type, Field("Value"): Payload() } return Message("TLV.Message", structure, types)
def test_dot_graph(tmp_path: Path) -> None: f_type = ModularInteger("P::T", Pow(Number(2), Number(32))) m = Message( "P::M", structure=[Link(INITIAL, Field("X")), Link(Field("X"), FINAL)], types={Field("X"): f_type}, ) expected = """ digraph "P::M" { graph [bgcolor="#00000000", pad="0.1", ranksep="0.1 equally", splines=true, truecolor=true]; edge [color="#6f6f6f", fontcolor="#6f6f6f", fontname="Fira Code", penwidth="2.5"]; node [color="#6f6f6f", fillcolor="#009641", fontcolor="#ffffff", fontname=Arimo, shape=box, style="rounded,filled", width="1.5"]; Initial [fillcolor="#ffffff", label="", shape=circle, width="0.5"]; X; intermediate_0 [color="#6f6f6f", fontcolor="#6f6f6f", fontname="Fira Code", height=0, label="(⊤, 32, ⋆)", penwidth=0, style="", width=0]; Initial -> intermediate_0 [arrowhead=none]; intermediate_0 -> X [minlen=1]; intermediate_1 [color="#6f6f6f", fontcolor="#6f6f6f", fontname="Fira Code", height=0, label="(⊤, 0, ⋆)", penwidth=0, style="", width=0]; X -> intermediate_1 [arrowhead=none]; intermediate_1 -> Final [minlen=1]; Final [fillcolor="#6f6f6f", label="", shape=circle, width="0.5"]; } """ assert_graph(create_message_graph(m), expected, tmp_path)
def test_dot_graph_with_condition() -> None: f_type = ModularInteger("P.T", Pow(Number(2), Number(32))) m = Message( "P.M", structure=[ Link(INITIAL, Field("F1")), Link(Field("F1"), FINAL, Greater(Variable("F1"), Number(100))), ], types={Field("F1"): f_type}, ) expected = """ digraph "P.M" { graph [ranksep="0.8 equally", splines=ortho]; edge [color="#6f6f6f", fontcolor="#6f6f6f", fontname="Fira Code"]; node [color="#6f6f6f", fillcolor="#009641", fontcolor="#ffffff", fontname=Arimo, shape=box, style="rounded,filled", width="1.5"]; Initial [fillcolor="#ffffff", label="", shape=circle, width="0.5"]; F1; Initial -> F1 [xlabel="(⊤, 32, ⋆)"]; F1 -> Final [xlabel="(F1 > 100, 0, ⋆)"]; Final [fillcolor="#6f6f6f", label="", shape=circle, width="0.5"]; } """ assert_graph(Graph(m), expected)
def test_message_multiple_duplicate_links() -> None: t = ModularInteger("P.T", Number(2)) x = Field(ID("X", location=Location((1, 5)))) y = Field(ID("Y", location=Location((2, 5)))) structure = [ Link(INITIAL, x), Link(x, y), Link(x, FINAL, location=Location((3, 16))), Link(x, FINAL, location=Location((4, 18))), Link(y, FINAL, location=Location((5, 20))), Link(y, FINAL, location=Location((6, 22))), ] types = {Field("X"): t, Field("Y"): t} assert_message_model_error( structure, types, f'^<stdin>:1:5: model: error: duplicate link from "X" to "{FINAL.name}"\n' f"<stdin>:3:16: model: info: duplicate link\n" f"<stdin>:4:18: model: info: duplicate link\n" f'<stdin>:2:5: model: error: duplicate link from "Y" to "{FINAL.name}"\n' f"<stdin>:5:20: model: info: duplicate link\n" f"<stdin>:6:22: model: info: duplicate link", )
def test_field_coverage_2(self) -> None: foo_type = ModularInteger("P.Foo", Pow(Number(2), Number(32))) structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2")), Link(Field("F2"), Field("F4"), Greater(Variable("F1"), Number(100))), Link( Field("F2"), Field("F3"), LessEqual(Variable("F1"), Number(100)), first=Add(Last("F2"), Number(64)), ), Link(Field("F3"), Field("F4")), Link(Field("F4"), FINAL), ] types = { Field("F1"): foo_type, Field("F2"): foo_type, Field("F3"): foo_type, Field("F4"): foo_type, } with mock.patch("rflx.model.Message._Message__verify_conditions", lambda x: None): with self.assertRaisesRegex( ModelError, "^path F1 -> F2 -> F3 -> F4 does not cover whole message"): Message("P.M", structure, types)
def parse_type(string: str, location: int, tokens: list) -> Type: try: if tokens[3] == 'mod': return ModularInteger(tokens[1], *tokens[4:6]) if tokens[3] == 'range': tokens[6] = tokens[6]['size'] return RangeInteger(tokens[1], *tokens[4:7]) if tokens[3] == 'message': return Message(tokens[1], tokens[4]) if tokens[3] == '(': elements = dict(tokens[4:-2]) aspects = tokens[-1] if len(elements) < len(tokens[4:-2]): raise ModelError(f'"{tokens[1]}" contains duplicate elements') if 'always_valid' not in aspects: aspects['always_valid'] = False return Enumeration(tokens[1], elements, aspects['size'], aspects['always_valid']) if tokens[3] == 'new': if len(tokens) == 7: tokens.append(TRUE) return Refinement(tokens[1], *tokens[4:]) if tokens[3] == 'array of': return Array(tokens[1], tokens[4]) except ModelError as e: raise ParseFatalException(string, location, e) raise ParseFatalException(string, location, 'unexpected type')
def test_type_derivation_spec() -> None: assert_specifications_string( """ package Test is type T is mod 256; type Foo is message N : T; end message; type Bar is new Foo; end Test; """, { "Test": Specification( ContextSpec([]), PackageSpec( "Test", [ ModularInteger("__PACKAGE__.T", Number(256)), MessageSpec("__PACKAGE__.Foo", [Component("N", "T")]), DerivationSpec("__PACKAGE__.Bar", "Foo"), ], ), ) }, )
def test_opaque_not_byte_aligned_dynamic() -> None: with pytest.raises( RecordFluxError, match= r'^<stdin>:44:3: model: error: opaque field "O2" not aligned to' r" 8 bit boundary [(]L1 -> O1 -> L2 -> O2[)]", ): o2 = Field(ID("O2", location=Location((44, 3)))) Message( "P.M", [ Link(INITIAL, Field("L1")), Link( Field("L1"), Field("O1"), length=Variable("L1"), condition=Equal(Mod(Variable("L1"), Number(8)), Number(0)), ), Link(Field("O1"), Field("L2")), Link(Field("L2"), o2, length=Number(128)), Link(o2, FINAL), ], { Field("L1"): MODULAR_INTEGER, Field("L2"): ModularInteger("P.T", Number(4)), Field("O1"): Opaque(), o2: Opaque(), }, )
def test_array_aggregate_out_of_range() -> None: array_type = Array("P.Array", ModularInteger("P.Element", Number(64))) f = Field("F") with pytest.raises( RecordFluxError, match= r"^<stdin>:44:3: model: error: aggregate element out of range 0 .. 63", ): Message( "P.M", [ Link(INITIAL, f, length=Number(18)), Link( f, FINAL, condition=Equal( Variable("F"), Aggregate(Number(1), Number(2), Number(64, location=Location((44, 3)))), ), ), ], {Field("F"): array_type}, )
def test_type_refinement_spec() -> None: spec = { "Message_Type": Specification( ContextSpec([]), PackageSpec( "Message_Type", [ ModularInteger("__PACKAGE__.T", Number(256)), MessageSpec( "__PACKAGE__.PDU", [ Component( "Foo", "T", [ Then( "Bar", UNDEFINED, UNDEFINED, LessEqual(Variable("Foo"), Number(30, 16)), ), Then( "Baz", UNDEFINED, UNDEFINED, Greater(Variable("Foo"), Number(30, 16)), ), ], ), Component("Bar", "T"), Component("Baz", "T"), ], ), MessageSpec( "__PACKAGE__.Simple_PDU", [Component("Bar", "T"), Component("Baz", "T")], ), MessageSpec("__PACKAGE__.Empty_PDU", []), ], ), ), "Type_Refinement": Specification( ContextSpec(["Message_Type"]), PackageSpec( "Type_Refinement", [ RefinementSpec( "Message_Type.Simple_PDU", "Bar", "Message_Type.PDU", Equal(Variable("Baz"), Number(42)), ), RefinementSpec("Message_Type.PDU", "Bar", "Message_Type.Simple_PDU"), ], ), ), } assert_specifications_files( [f"{TESTDIR}/message_type.rflx", f"{TESTDIR}/type_refinement.rflx"], spec )
def modular_integers( draw: Draw, unique_identifiers: ty.Generator[ID, None, None], multiple_of_8: bool = False, align_to_8: int = 0, ) -> ModularInteger: return ModularInteger( next(unique_identifiers), expr.Pow(expr.Number(2), expr.Number(draw(sizes(multiple_of_8, align_to_8)))), )
def test_name_conflict_between_literal_and_type() -> None: assert_model_error( [ Enumeration( "P::T", [ (ID("FOO", Location((3, 27))), Number(1)), (ID("BAR", Location((3, 32))), Number(2)), ], Number(1), always_valid=False, ), ModularInteger("P::Foo", Number(256), Location((4, 16))), ModularInteger("P::Bar", Number(256), Location((5, 16))), ], r'<stdin>:3:27: model: error: literal "FOO" conflicts with type declaration\n' r'<stdin>:4:16: model: info: conflicting type "P::Foo"\n' r'<stdin>:3:32: model: error: literal "BAR" conflicts with type declaration\n' r'<stdin>:5:16: model: info: conflicting type "P::Bar"', )
def test_name_conflict_between_literal_and_type() -> None: assert_model_error( [ Enumeration( "P.T", [ (ID("Foo", Location((3, 27))), Number(1)), (ID("Bar", Location((3, 32))), Number(2)), ], Number(1), False, ), ModularInteger("T.Foo", Number(256), Location((4, 16))), ModularInteger("T.Bar", Number(256), Location((5, 16))), ], r'<stdin>:3:32: model: error: literal conflicts with type "Bar"\n' r"<stdin>:5:16: model: info: conflicting type declaration\n" r'<stdin>:3:27: model: error: literal conflicts with type "Foo"\n' r"<stdin>:4:16: model: info: conflicting type declaration", )
def test_message_superfluous_type(self) -> None: t = ModularInteger("P.T", Number(2)) structure = [ Link(INITIAL, Field("X")), Link(Field("X"), FINAL), ] types = {Field("X"): t, Field("Y"): t} with self.assertRaisesRegex(ModelError, '^superfluous field "Y" in field types of "P.M"$'): Message("P.M", structure, types)