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_enumeration_invalid_literal() -> None: assert_type_error( Enumeration("P.T", [("A B", Number(1))], Number(8), False, Location(((1, 2)))), r'^<stdin>:1:2: model: error: invalid literal name "A B" in "T"$', ) assert_type_error( Enumeration("P.T", [("A.B", Number(1))], Number(8), False, Location((6, 4))), r'^<stdin>:6:4: model: error: invalid literal name "A.B" in "T"$', )
def test_enumeration_invalid_literal_value() -> None: assert_type_error( Enumeration("P.T", [("A", Number(2**63))], Number(64), False, Location((10, 5))), r'^<stdin>:10:5: model: error: enumeration value of "T"' r" outside of permitted range \(0 .. 2\*\*63 - 1\)$", )
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 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 enumerations( draw: Draw, unique_identifiers: ty.Generator[ID, None, None], multiple_of_8: bool = False, align_to_8: int = 0, ) -> Enumeration: @st.composite def literal_identifiers(_: ty.Callable[[], object]) -> str: assert unique_identifiers return str(next(unique_identifiers).name) size = draw(sizes(multiple_of_8, align_to_8)) literals = draw( st.lists( st.tuples( literal_identifiers(), st.builds( expr.Number, st.integers(min_value=0, max_value=min(2**size - 1, 2**63 - 1)) ), ), unique_by=(lambda x: x[0], lambda x: x[1]), # type: ignore[no-any-return] min_size=1, max_size=2**size, ) ) return Enumeration( next(unique_identifiers), literals, expr.Number(size), draw(st.booleans()) if len(literals) < 2**size else False, )
def test_invalid_enumeration_type_identical_literals() -> None: assert_model_error( [ Enumeration( "P::T1", [("Foo", Number(1)), (ID("Bar", Location((3, 33))), Number(2))], Number(1), always_valid=False, ), Enumeration( "P::T2", [("Bar", Number(1)), ("Baz", Number(2))], Number(1), always_valid=False, location=Location((4, 16)), ), ], r"<stdin>:4:16: model: error: conflicting literals: Bar\n" r'<stdin>:3:33: model: info: previous occurrence of "Bar"', )
def test_invalid_enumeration_type_duplicate_elements() -> None: assert_type_error( Enumeration( "P.T", [(ID("Foo", Location((3, 27))), Number(1)), (ID("Foo", Location((3, 32))), Number(2))], Number(1), False, ), r'<stdin>:3:32: model: error: duplicate literal "Foo"\n' r"<stdin>:3:27: model: info: previous occurrence", )
def create_enumeration_pdu() -> PDU: priority_type = Enumeration('Priority', {'LOW': Number(1), 'MEDIUM': Number(4), 'HIGH': Number(7)}, Number(3), True) initial = InitialNode() priority = Node('Priority', priority_type) initial.edges = [Edge(priority)] priority.edges = [Edge(FINAL)] return PDU('Enumeration.Message', initial)
def test_invalid_type_condition_enum() -> None: structure = [ Link(INITIAL, Field("F1")), Link( Field("F1"), Field("F2"), condition=Equal(Variable("F1"), Variable("E4"), location=Location((22, 10))), ), Link(Field("F2"), FINAL), ] e1 = Enumeration( "P.E1", [("E1", Number(1)), ("E2", Number(2)), ("E3", Number(3))], Number(8), False, Location((10, 4)), ) e2 = Enumeration( "P.E2", [("E4", Number(1)), ("E5", Number(2)), ("E6", Number(3))], Number(8), False, Location((11, 4)), ) types = { Field("F1"): e1, Field("F2"): e2, } assert_message_model_error( structure, types, r"^" r"<stdin>:22:10: model: error: comparison of incompatible enumeration literals\n" r'<stdin>:10:4: model: info: of type "P.E1"\n' r'<stdin>:11:4: model: info: and type "P.E2"' r"$", )
def test_enumeration_type_spec() -> None: spec = { "Enumeration_Type": Specification( ContextSpec([]), PackageSpec( "Enumeration_Type", [ Enumeration( "__PACKAGE__.Day", [ ("Mon", Number(1)), ("Tue", Number(2)), ("Wed", Number(3)), ("Thu", Number(4)), ("Fri", Number(5)), ("Sat", Number(6)), ("Sun", Number(7)), ], Number(3), False, ), Enumeration( "__PACKAGE__.Gender", [("M", Number(0)), ("F", Number(1))], Number(1), False, ), Enumeration( "__PACKAGE__.Priority", [("LOW", Number(1)), ("MEDIUM", Number(4)), ("HIGH", Number(7))], Number(8), True, ), ], ), ) } assert_specifications_files([f"{TESTDIR}/enumeration_type.rflx"], spec)
def test_nonexistent_variable(self) -> None: mod_type = ModularInteger("P.MT", Pow(Number(2), Number(32))) enum_type = Enumeration("P.ET", {"Val1": Number(0), "Val2": Number(1)}, Number(8), True) structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), Equal(Variable("F1"), Variable("Val3"))), Link(Field("F2"), FINAL), ] types = {Field("F1"): enum_type, Field("F2"): mod_type} with self.assertRaisesRegex( ModelError, '^undefined variable "Val3" referenced in ' + 'condition 0 from field "F1" to "F2"', ): Message("P.M", structure, types)
def test_invalid_enumeration_type_builtin_literals() -> None: assert_model_error( [ Enumeration( "P.T", [("True", Number(1)), ("False", Number(2))], Number(1), False, Location((3, 16)), ), ], r"<stdin>:3:16: model: error: conflicting literals: False, True\n" r'__BUILTINS__:0:0: model: info: previous occurrence of "False"\n' r'__BUILTINS__:0:0: model: info: previous occurrence of "True"', )
def test_conflicting_literal_builtin_type() -> None: assert_model_error( [ Enumeration( "P.T", [ (ID("E1", Location((3, 27))), Number(1)), (ID("Boolean", Location((3, 31))), Number(2)), ], Number(8), False, ), ], r'<stdin>:3:31: model: error: literal conflicts with type "Boolean"\n' r"__BUILTINS__:0:0: model: info: conflicting type declaration", )
def create_enumeration_message() -> Message: priority_type = Enumeration( "Enumeration.Priority", { "LOW": Number(1), "MEDIUM": Number(4), "HIGH": Number(7) }, Number(3), True, ) structure = [ Link(INITIAL, Field("Priority")), Link(Field("Priority"), FINAL) ] types = {Field("Priority"): priority_type} return Message("Enumeration.Message", structure, types)
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_nonexistent_variable() -> None: mod_type = ModularInteger("P.MT", Pow(Number(2), Number(32))) enum_type = Enumeration("P.ET", [("Val1", Number(0)), ("Val2", Number(1))], Number(8), True) structure = [ Link(INITIAL, Field("F1")), Link( Field("F1"), Field("F2"), Equal(Variable("F1"), Variable("Val3", location=Location((444, 55)))), ), Link(Field("F2"), FINAL), ] types = {Field("F1"): enum_type, Field("F2"): mod_type} assert_message_model_error( structure, types, '^<stdin>:444:55: model: error: undefined variable "Val3" referenced$', )
def test_message_name_conflict_field_enum() -> None: t = Enumeration( "P.T", [(ID("X", Location((3, 27))), Number(1)), (ID("Y", Location((3, 32))), Number(2))], Number(8), False, ) structure = [ Link(INITIAL, Field("X")), Link(Field("X"), FINAL), ] types = {Field(ID("X", Location((5, 6)))): t} assert_message_model_error( structure, types, '^<stdin>:5:6: model: error: name conflict for field "X" in "P.M"\n' "<stdin>:3:27: model: info: conflicting enumeration literal", )
def test_exclusive_prefixed_enum_valid() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), FINAL, condition=Equal(Variable("F1"), Variable("ONE"))), Link(Field("F1"), Field("F2"), condition=Equal(Variable("F1"), Variable("P.TWO"))), Link(Field("F2"), FINAL), ] types = { Field("F1"): ENUMERATION, Field("F2"): Enumeration( "P2.Enumeration", [("ONE", Number(2)), ("TWO", Number(1))], Number(8), False, ), } Message("P.M", structure, types)
def parse_type(string: str, location: int, tokens: ParseResults) -> Type: package = ID("__PACKAGE__") name = tokens[1] locn = parser_location(tokens[0], tokens[-1], string) identifier = package * name if tokens[3] == "mod": return ModularInteger(identifier, tokens[4], locn) if tokens[3] == "range": tokens[6] = tokens[6]["size"] return RangeInteger(identifier, tokens[4], tokens[5], tokens[6], locn) if tokens[3] == "message": return MessageSpec(identifier, tokens[4], locn) if tokens[3] == "null message": return MessageSpec(identifier, [], locn) if tokens[3] == "(": elements = tokens[4:-3] aspects = tokens[-2] if "always_valid" not in aspects: aspects["always_valid"] = False enumeration = Enumeration(identifier, elements, aspects["size"], aspects["always_valid"], locn) return enumeration if tokens[3] == "new": return DerivationSpec(identifier, tokens[4], locn) if tokens[3] == "array of": return ArraySpec( identifier, ReferenceSpec(qualified_type_name(tokens[4], package), tokens[4].location), locn, ) raise ParseFatalException(string, location, "unexpected type")
def fixture_enum_value_imported() -> EnumValue: return EnumValue( Enumeration("Test.Enum", [("One", Number(1)), ("Two", Number(2))], Number(8), False), imported=True, )
DerivedMessage, Enumeration, Field, Link, Message, Model, ModularInteger, Opaque, RangeInteger, Refinement, ) NULL_MESSAGE = Message("Null.Message", [], {}) NULL_MODEL = Model([NULL_MESSAGE]) TLV_TAG = Enumeration("TLV.Tag", [("Msg_Data", Number(1)), ("Msg_Error", Number(3))], Number(2), False) TLV_LENGTH = ModularInteger("TLV.Length", Pow(Number(2), Number(14))) TLV_MESSAGE = Message( "TLV.Message", [ 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), ], {
def test_enumeration_invalid_always_valid_aspect() -> None: with pytest.raises( RecordFluxError, match=r'^model: error: unnecessary always-valid aspect on "T"$'): Enumeration("P.T", [("A", Number(0)), ("B", Number(1))], Number(1), True).error.propagate()
def test_enumeration_invalid_size_variable() -> None: assert_type_error( Enumeration("P.T", [("A", Number(1))], Add(Number(8), Variable("X")), False, Location((34, 3))), r'^<stdin>:34:3: model: error: size of "T" contains variable$', )
def test_consistency_specification_parsing_generation(tmp_path: Path) -> None: tag = Enumeration( "Test::Tag", [("Msg_Data", expr.Number(1)), ("Msg_Error", expr.Number(3))], expr.Number(8), always_valid=False, ) length = ModularInteger("Test::Length", expr.Pow(expr.Number(2), expr.Number(16))) message = Message( "Test::Message", [ Link(INITIAL, Field("Tag")), Link( Field("Tag"), Field("Length"), expr.Equal(expr.Variable("Tag"), expr.Variable("Msg_Data")), ), Link(Field("Tag"), FINAL, expr.Equal(expr.Variable("Tag"), expr.Variable("Msg_Error"))), Link( Field("Length"), Field("Value"), size=expr.Mul(expr.Variable("Length"), expr.Number(8)), ), Link(Field("Value"), FINAL), ], { Field("Tag"): tag, Field("Length"): length, Field("Value"): OPAQUE }, skip_proof=True, ) session = Session( "Test::Session", "A", "C", [ State( "A", declarations=[], actions=[stmt.Read("X", expr.Variable("M"))], transitions=[ Transition("B"), ], ), State( "B", declarations=[ decl.VariableDeclaration("Z", BOOLEAN.identifier, expr.Variable("Y")), ], actions=[], transitions=[ Transition( "C", condition=expr.And( expr.Equal(expr.Variable("Z"), expr.TRUE), expr.Equal(expr.Call("G", [expr.Variable("F")]), expr.TRUE), ), description="rfc1149.txt+45:4-47:8", ), Transition("A"), ], description="rfc1149.txt+51:4-52:9", ), State("C"), ], [ decl.VariableDeclaration("M", "Test::Message"), decl.VariableDeclaration("Y", BOOLEAN.identifier, expr.FALSE), ], [ decl.ChannelDeclaration("X", readable=True, writable=True), decl.TypeDeclaration(Private("Test::T")), decl.FunctionDeclaration("F", [], "Test::T"), decl.FunctionDeclaration("G", [decl.Argument("P", "Test::T")], BOOLEAN.identifier), ], [BOOLEAN, OPAQUE, tag, length, message], ) model = Model(types=[BOOLEAN, OPAQUE, tag, length, message], sessions=[session]) model.write_specification_files(tmp_path) p = parser.Parser() p.parse(tmp_path / "test.rflx") parsed_model = p.create_model() assert parsed_model.types == model.types assert parsed_model.sessions == model.sessions assert parsed_model == model
def test_enumeration_invalid_size_too_small() -> None: assert_type_error( Enumeration("P.T", [("A", Number(256))], Number(8), False, Location((10, 5))), r'^<stdin>:10:5: model: error: size of "T" too small$', )
Model, ModularInteger, RangeInteger, Refinement, Sequence, Session, State, Transition, UnprovenMessage, ) NULL_MESSAGE = Message("Null::Message", [], {}, skip_proof=True) NULL_MODEL = Model([NULL_MESSAGE]) TLV_TAG = Enumeration( "TLV::Tag", [("Msg_Data", Number(1)), ("Msg_Error", Number(3))], Number(8), always_valid=False ) TLV_LENGTH = ModularInteger("TLV::Length", Pow(Number(2), Number(16))) TLV_MESSAGE = Message( "TLV::Message", [ 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"), size=Mul(Variable("Length"), Number(8))), Link(Field("Value"), FINAL), ], {Field("Tag"): TLV_TAG, Field("Length"): TLV_LENGTH, Field("Value"): OPAQUE}, skip_proof=True, ) TLV_MODEL = Model([TLV_TAG, TLV_LENGTH, TLV_MESSAGE])
def test_enumeration_invalid_size_exceeds_limit() -> None: assert_type_error( Enumeration("P.T", [("A", Number(256))], Number(128), False, Location((8, 20))), r'^<stdin>:8:20: model: error: size of "T" exceeds limit \(2\*\*64\)$', )