def test_conditionally_unreachable_field_outgoing_multi() -> None: f2 = Field(ID("F2", Location((90, 12)))) structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), f2, LessEqual(Variable("F1"), Number(32), Location((66, 3)))), Link(Field("F1"), Field("F3"), Greater(Variable("F1"), Number(32))), Link( f2, Field("F3"), And( Greater(Variable("F1"), Number(32)), LessEqual(Variable("F1"), Number(48)), location=Location((22, 34)), ), ), Link(f2, FINAL, Greater(Variable("F1"), Number(48))), Link(Field("F3"), FINAL), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, Field("F3"): MODULAR_INTEGER, } assert_message_model_error( structure, types, r"^" r'<stdin>:90:12: model: error: unreachable field "F2" in "P.M"\n' r"<stdin>:90:12: model: info: path 0 [(]F1 -> F2[)]:\n" r'<stdin>:66:3: model: info: unsatisfied "F1 <= 32"\n' r'<stdin>:90:12: model: info: unsatisfied "[(]F1 > 32 and F1 <= 48[)] or F1 > 48"', )
def public_context_predicate() -> Expr: return And( GreaterEqual(Call(const.TYPES_BYTE_INDEX, [Variable("First")]), Variable("Buffer_First")), LessEqual(Call(const.TYPES_BYTE_INDEX, [Variable("Last")]), Variable("Buffer_Last")), LessEqual(Variable("First"), Variable("Last")), LessEqual(Variable("Last"), Div(Last(const.TYPES_BIT_INDEX), Number(2))), )
def test_prefixed_message() -> None: assert_equal( UnprovenMessage( "P.M", [ Link(INITIAL, Field("F1")), Link( Field("F1"), Field("F2"), LessEqual(Variable("F1"), Number(100)), first=First("F1"), ), Link( Field("F1"), Field("F3"), GreaterEqual(Variable("F1"), Number(200)), first=First("F1"), ), Link(Field("F2"), FINAL), Link(Field("F3"), Field("F4"), length=Variable("F3")), Link(Field("F4"), FINAL), ], { Field("F1"): deepcopy(MODULAR_INTEGER), Field("F2"): deepcopy(MODULAR_INTEGER), Field("F3"): deepcopy(RANGE_INTEGER), Field("F4"): Opaque(), }, ).prefixed("X_"), UnprovenMessage( "P.M", [ Link(INITIAL, Field("X_F1")), Link( Field("X_F1"), Field("X_F2"), LessEqual(Variable("X_F1"), Number(100)), first=First("X_F1"), ), Link( Field("X_F1"), Field("X_F3"), GreaterEqual(Variable("X_F1"), Number(200)), first=First("X_F1"), ), Link(Field("X_F2"), FINAL), Link(Field("X_F3"), Field("X_F4"), length=Variable("X_F3")), Link(Field("X_F4"), FINAL), ], { Field("X_F1"): deepcopy(MODULAR_INTEGER), Field("X_F2"): deepcopy(MODULAR_INTEGER), Field("X_F3"): deepcopy(RANGE_INTEGER), Field("X_F4"): Opaque(), }, ), )
def test_merge_message_recursive() -> None: assert_equal( deepcopy(M_DBL_REF).merged(), UnprovenMessage( "P.Dbl_Ref", [ Link(INITIAL, Field("SR_NR_F1"), length=Number(16)), Link( Field("SR_NR_F3"), Field("NR_F1"), Equal(Variable("SR_NR_F3"), Variable("P.ONE")), length=Number(16), ), Link(Field("SR_NR_F4"), Field("NR_F1"), length=Number(16)), Link(Field("NR_F3"), FINAL, Equal(Variable("NR_F3"), Variable("P.ONE"))), Link(Field("NR_F4"), FINAL), Link(Field("SR_NR_F1"), Field("SR_NR_F2")), Link( Field("SR_NR_F2"), Field("SR_NR_F3"), LessEqual(Variable("SR_NR_F2"), Number(100)), first=First("SR_NR_F2"), ), Link( Field("SR_NR_F2"), Field("SR_NR_F4"), GreaterEqual(Variable("SR_NR_F2"), Number(200)), first=First("SR_NR_F2"), ), Link(Field("NR_F1"), Field("NR_F2")), Link( Field("NR_F2"), Field("NR_F3"), LessEqual(Variable("NR_F2"), Number(100)), first=First("NR_F2"), ), Link( Field("NR_F2"), Field("NR_F4"), GreaterEqual(Variable("NR_F2"), Number(200)), first=First("NR_F2"), ), ], { Field("SR_NR_F1"): Opaque(), Field("SR_NR_F2"): deepcopy(MODULAR_INTEGER), Field("SR_NR_F3"): deepcopy(ENUMERATION), Field("SR_NR_F4"): deepcopy(RANGE_INTEGER), Field("NR_F1"): Opaque(), Field("NR_F2"): deepcopy(MODULAR_INTEGER), Field("NR_F3"): deepcopy(ENUMERATION), Field("NR_F4"): deepcopy(RANGE_INTEGER), }, ), )
def constraints(self, name: str, proof: bool = False) -> Expr: if proof: return And( GreaterEqual(Variable(name), self.first), LessEqual(Variable(name), self.last) ) c: Expr = TRUE if self.first.simplified() != self.base_first.simplified(): c = GreaterEqual(Variable(name), self.first) if self.last.simplified() != self.base_last.simplified(): c = And(c, LessEqual(Variable(name), self.last)) return c.simplified()
def test_no_valid_path() -> None: f1 = Field(ID("F1", Location((10, 5)))) f2 = Field(ID("F2", Location((11, 6)))) f3 = Field(ID("F3", Location((12, 7)))) structure = [ Link(INITIAL, f1), Link(f1, f2, condition=LessEqual(Variable("F1"), Number(80), Location( (20, 2)))), Link(f1, f3, condition=Greater(Variable("F1"), Number(80), Location((21, 3)))), Link(f2, f3, condition=Greater(Variable("F1"), Number(80), Location((22, 4)))), Link(f3, FINAL, condition=LessEqual(Variable("F1"), Number(80), Location( (23, 5)))), ] types = { Field("F1"): RANGE_INTEGER, Field("F2"): RANGE_INTEGER, Field("F3"): RANGE_INTEGER, } assert_message_model_error( structure, types, r"^" r'<stdin>:11:6: model: error: unreachable field "F2" in "P.M"\n' r"<stdin>:11:6: model: info: path 0 [(]F1 -> F2[)]:\n" r'<stdin>:20:2: model: info: unsatisfied "F1 <= 80"\n' r'<stdin>:11:6: model: info: unsatisfied "F1 > 80"\n' r'<stdin>:12:7: model: error: unreachable field "F3" in "P.M"\n' r"<stdin>:12:7: model: info: path 0 [(]F1 -> F2 -> F3[)]:\n" r'<stdin>:20:2: model: info: unsatisfied "F1 <= 80"\n' r'<stdin>:22:4: model: info: unsatisfied "F1 > 80"\n' r"<stdin>:12:7: model: info: path 1 [(]F1 -> F3[)]:\n" r'<stdin>:21:3: model: info: unsatisfied "F1 > 80"\n' r'<stdin>:12:7: model: info: unsatisfied "F1 <= 80"\n' r'model: error: unreachable field "Final" in "P.M"\n' r"model: info: path 0 [(]F1 -> F2 -> F3 -> Final[)]:\n" r'<stdin>:20:2: model: info: unsatisfied "F1 <= 80"\n' r'<stdin>:22:4: model: info: unsatisfied "F1 > 80"\n' r"model: info: path 1 [(]F1 -> F3 -> Final[)]:\n" r'<stdin>:21:3: model: info: unsatisfied "F1 > 80"\n' r'<stdin>:23:5: model: info: unsatisfied "F1 <= 80"', )
def test_merge_message_simple_derived() -> None: assert_equal( deepcopy(M_SMPL_REF_DERI).merged(), UnprovenDerivedMessage( "P.Smpl_Ref_Deri", M_SMPL_REF, [ Link(INITIAL, Field("NR_F1"), length=Number(16)), Link(Field("NR_F3"), FINAL, Equal(Variable("NR_F3"), Variable("P.ONE"))), Link(Field("NR_F4"), FINAL), Link(Field("NR_F1"), Field("NR_F2")), Link( Field("NR_F2"), Field("NR_F3"), LessEqual(Variable("NR_F2"), Number(100)), first=First("NR_F2"), ), Link( Field("NR_F2"), Field("NR_F4"), GreaterEqual(Variable("NR_F2"), Number(200)), first=First("NR_F2"), ), ], { Field("NR_F1"): Opaque(), Field("NR_F2"): deepcopy(MODULAR_INTEGER), Field("NR_F3"): deepcopy(ENUMERATION), Field("NR_F4"): deepcopy(RANGE_INTEGER), }, ), )
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_message_unsupported_expression() -> None: x = Field("X") structure = [ Link(INITIAL, x), Link( x, FINAL, condition=LessEqual( Pow( Number(2), Add(Variable("X", location=Location((10, 23))), Number(1)), location=Location((10, 19)), ), Number(1024), ), ), ] types = {x: MODULAR_INTEGER} assert_message_model_error( structure, types, '^<stdin>:10:19: model: error: unsupported expression in "P.M"\n' '<stdin>:10:23: model: info: variable "X" in exponent', )
def __init__(self, name: str, first: MathExpr, last: MathExpr, size: MathExpr) -> None: first_num = first.simplified() if not isinstance(first_num, Number): raise ModelError(f'first of "{name}" contains variable') last_num = last.simplified() if not isinstance(last_num, Number): raise ModelError(f'last of "{name}" contains variable') if first_num < Number(0): raise ModelError(f'first of "{name}" negative') if first_num > last_num: raise ModelError(f'range of "{name}" negative') size_num = size.simplified() if not isinstance(size_num, Number): raise ModelError(f'size of "{name}" contains variable') if log(int(last_num) + 1) / log(2) > int(size_num): raise ModelError(f'size for "{name}" too small') super().__init__(name) self.__first = first self.__last = last self.__size = size constraints: LogExpr = TRUE if self.first.simplified() != self.base_first.simplified(): constraints = GreaterEqual(Value(self.name), self.first) if self.last.simplified() != self.base_last.simplified(): constraints = And(constraints, LessEqual(Value(self.name), self.last)) self.__constraints = constraints.simplified()
def test_no_path_to_final_transitive() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2")), Link(Field("F2"), Field("F3"), Greater(Variable("F1"), Number(100))), Link(Field("F3"), FINAL), Link(Field("F2"), Field("F4"), LessEqual(Variable("F1"), Number(100))), Link(Field("F4"), Field("F5")), Link(Field("F5"), Field("F6")), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, Field("F3"): MODULAR_INTEGER, Field("F4"): MODULAR_INTEGER, Field("F5"): MODULAR_INTEGER, Field("F6"): MODULAR_INTEGER, } assert_message_model_error( structure, types, r"^" r'model: error: no path to FINAL for field "F4" in "P.M"\n' r'model: error: no path to FINAL for field "F5" in "P.M"\n' r'model: error: no path to FINAL for field "F6" in "P.M"' r"$", )
def test_field_coverage_2(monkeypatch: Any) -> None: 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"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, Field("F3"): MODULAR_INTEGER, Field("F4"): MODULAR_INTEGER, } monkeypatch.setattr(Message, "_AbstractMessage__verify_conditions", lambda x: None) assert_message_model_error( structure, types, r"^" r"model: error: path does not cover whole message\n" r'model: info: on path: "F1"\n' r'model: info: on path: "F2"\n' r'model: info: on path: "F3"\n' r'model: info: on path: "F4"' r"$", )
def test_message_field_condition(self) -> None: self.assertEqual(ETHERNET_FRAME.field_condition(INITIAL), TRUE) self.assertEqual( ETHERNET_FRAME.field_condition(Field("TPID")), Equal(Variable("Type_Length_TPID"), Number(33024, 16)), ) self.assertEqual( ETHERNET_FRAME.field_condition(Field("Type_Length")), Or( NotEqual(Variable("Type_Length_TPID"), Number(33024, 16)), Equal(Variable("Type_Length_TPID"), Number(33024, 16)), ), ) self.assertEqual( ETHERNET_FRAME.field_condition(Field("Payload")), Or( And( Or( NotEqual(Variable("Type_Length_TPID"), Number(33024, 16)), Equal(Variable("Type_Length_TPID"), Number(33024, 16)), ), LessEqual(Variable("Type_Length"), Number(1500)), ), And( Or( NotEqual(Variable("Type_Length_TPID"), Number(33024, 16)), Equal(Variable("Type_Length_TPID"), Number(33024, 16)), ), GreaterEqual(Variable("Type_Length"), Number(1536)), ), ), )
def test_message_incoming(self) -> None: self.assertEqual(ETHERNET_FRAME.incoming(INITIAL), []) self.assertEqual( ETHERNET_FRAME.incoming(Field("Type_Length")), [ Link( Field("Type_Length_TPID"), Field("Type_Length"), NotEqual(Variable("Type_Length_TPID"), Number(0x8100, 16)), first=First("Type_Length_TPID"), ), Link(Field("TCI"), Field("Type_Length")), ], ) self.assertEqual( ETHERNET_FRAME.incoming(FINAL), [ Link( Field("Payload"), FINAL, And( GreaterEqual(Div(Length("Payload"), Number(8)), Number(46)), LessEqual(Div(Length("Payload"), Number(8)), Number(1500)), ), ) ], )
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 __prove_coverage(self) -> None: """ Prove that the fields of a message cover all message bits, i.e. there are no holes in the message definition. Idea: Let f be the bits covered by the message. By definition (1) f >= Message'First and f <= Message'Last holds. For every field add a conjunction of the form (2) Not(f >= Field'First and f <= Field'Last), effectively pruning the range that this field covers from the bit range of the message. For the overall expression, prove that it is false for all f, i.e. no bits are left. """ for path in [p[:-1] for p in self.__paths[FINAL] if p]: # Calculate (1) message_range = And( GreaterEqual(Variable("f"), First("Message")), LessEqual(Variable("f"), Last("Message")), ) # Calculate (2) for all fields fields = And( *[ Not( And( GreaterEqual(Variable("f"), self.__target_first(l)), LessEqual(Variable("f"), self.__target_last(l)), ) ) for l in path ] ) # Define that the end of the last field of a path is the end of the message last_field = Equal(self.__target_last(path[-1]), Last("Message")) # Constraints for links and types path_expressions = self.__with_constraints( And(*[self.__link_expression(l) for l in path]) ) # Coverage expression must be False, i.e. no bits left coverage = Not(And(*[fields, last_field, path_expressions, message_range])) result = coverage.forall() if result != ProofResult.sat: path_message = " -> ".join([l.target.name for l in path]) message = str(coverage).replace("\n\t", "") raise ModelError( f"path {path_message} does not cover whole message" f' in "{self.full_name}" ({result}: {message})' )
def bounded_composite_setter_preconditions(message: Message, field: Field) -> Sequence[Expr]: return [ Call( "Field_Condition", [ Variable("Ctx"), NamedAggregate(("Fld", Variable(field.affixed_name))) ] + ([Variable("Length")] if common.length_dependent_condition(message) else []), ), GreaterEqual( Call("Available_Space", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), LessEqual( Add( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), Div(Last(const.TYPES_BIT_INDEX), Number(2)), ), Or(*[ And( *[ Call("Valid", [Variable("Ctx"), Variable(field.affixed_name)]) for field in message.fields if Variable(field.name) in l.condition.variables() ], l.condition.substituted( mapping={ Variable(field.name): Call(f"Get_{field.name}", [Variable("Ctx")]) for field in message.fields if Variable(field.name) in l.condition.variables() }), ) for l in message.incoming(field) if Last("Message") in l.length ]), Equal( Mod( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Size(const.TYPES_BYTE), ), Number(1), ), Equal( Mod(Variable("Length"), Size(const.TYPES_BYTE)), Number(0), ), ]
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 buffer_constraints(last: MathExpr) -> LogExpr: last = last.simplified() index_constraint = LessEqual(First('Buffer'), Div(Last('Types.Index_Type'), Number(2))) if last != Last('Buffer'): length_constraint = GreaterEqual( Length('Buffer'), Add(last, -First('Buffer'), Number(1))) return And(length_constraint, index_constraint) return index_constraint
def __prove_conflicting_conditions(self) -> None: for f in (INITIAL, *self.__fields): conflict = LessEqual( Add( *[ If([(self.__with_constraints(c.condition), Number(1))], Number(0)) for c in self.outgoing(f) ] ), Number(1), ) result = conflict.forall() if result != ProofResult.sat: message = str(conflict).replace("\n", "") raise ModelError( f'conflicting conditions for field "{f.name}"' f' in "{self.full_name}" ({result}: {message})' )
def setter_preconditions(self, field: Field) -> Sequence[Expr]: return [ VALID_CONTEXT, Not(Constrained("Ctx")), Call("Has_Buffer", [Name("Ctx")]), Call("Valid_Next", [Name("Ctx"), Name(field.affixed_name)]), LessEqual( Call("Field_Last", [Name("Ctx"), Name(field.affixed_name)]), Div(Last(self.types.bit_index), Number(2)), ), ]
def setter_preconditions(field: Field) -> Sequence[Expr]: return [ Not(Constrained("Ctx")), Call("Has_Buffer", [Variable("Ctx")]), Call("Valid_Next", [Variable("Ctx"), Variable(field.affixed_name)]), LessEqual( Call("Field_Last", [Variable("Ctx"), Variable(field.affixed_name)]), Div(Last(const.TYPES_BIT_INDEX), Number(2)), ), ]
def parse_relation(string: str, location: int, tokens: list) -> Relation: if tokens[1] == '<': return Less(tokens[0], tokens[2]) if tokens[1] == '<=': return LessEqual(tokens[0], tokens[2]) if tokens[1] == '=': return Equal(tokens[0], tokens[2]) if tokens[1] == '>=': return GreaterEqual(tokens[0], tokens[2]) if tokens[1] == '>': return Greater(tokens[0], tokens[2]) if tokens[1] == '/=': return NotEqual(tokens[0], tokens[2]) raise ParseFatalException(string, location, 'unexpected relation operator')
def parse_relation(string: str, location: int, tokens: ParseResults) -> Relation: if tokens[1] == "<": return Less(tokens[0], tokens[2]) if tokens[1] == "<=": return LessEqual(tokens[0], tokens[2]) if tokens[1] == "=": return Equal(tokens[0], tokens[2]) if tokens[1] == ">=": return GreaterEqual(tokens[0], tokens[2]) if tokens[1] == ">": return Greater(tokens[0], tokens[2]) if tokens[1] == "/=": return NotEqual(tokens[0], tokens[2]) raise ParseFatalException(string, location, "unexpected relation operator")
def test_exclusive_valid() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), FINAL, condition=Greater(Variable("F1"), Number(80))), Link(Field("F1"), Field("F2"), condition=LessEqual(Variable("F1"), Number(80))), Link(Field("F2"), FINAL), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, } Message("P.M", structure, types)
def create_ethernet_pdu() -> PDU: uint48 = ModularInteger('UINT48', Pow(Number(2), Number(48))) uint16 = RangeInteger('UINT16', Number(0), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16)) payload_array = Array('Payload_Array') initial = InitialNode() destination = Node('Destination', uint48) source = Node('Source', uint48) tpid = Node('TPID', uint16) tci = Node('TCI', uint16) ether_type = Node('EtherType', uint16) payload = Node('Payload', payload_array) initial.edges = [Edge(destination)] destination.edges = [Edge(source)] source.edges = [Edge(tpid)] tpid.edges = [Edge(tci, Equal(Value('TPID'), Number(0x8100))), Edge(ether_type, NotEqual(Value('TPID'), Number(0x8100)), first=First('TPID'))] tci.edges = [Edge(ether_type)] ether_type.edges = [Edge(payload, LessEqual(Value('EtherType'), Number(1500)), Mul(LengthValue('EtherType'), Number(8))), Edge(payload, GreaterEqual(Value('EtherType'), Number(1536)), Sub(Last('Message'), Last('EtherType')))] payload.edges = [Edge(FINAL, And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))))] return PDU('Ethernet.Frame', initial)
def test_message_invalid_relation_to_aggregate() -> None: structure = [ Link(INITIAL, Field("F1"), length=Number(16)), Link( Field("F1"), FINAL, LessEqual(Variable("F1"), Aggregate(Number(1), Number(2)), Location((100, 20))), ), ] types = {Field("F1"): Opaque()} assert_message_model_error( structure, types, r'^<stdin>:100:20: model: error: invalid relation " <= " between Opaque and Aggregate$', )
def test_tlv_valid_enum() -> None: structure = [ Link(INITIAL, Field("L")), Link(Field("L"), Field("T")), Link( Field("T"), Field("V"), length=Mul(Number(8), Variable("L")), condition=And(NotEqual(Variable("T"), Variable("TWO")), LessEqual(Variable("L"), Number(8192))), ), Link(Field("V"), FINAL), ] types = { Field("L"): RANGE_INTEGER, Field("T"): ENUMERATION, Field("V"): Opaque(), } Message("P.M", structure, types)
def parse_relation(string: str, location: int, tokens: ParseResults) -> Relation: def locn() -> Location: return Location(tokens[0].location.start, tokens[0].location.source, tokens[2].location.end) if tokens[1] == "<": return Less(tokens[0], tokens[2], locn()) if tokens[1] == "<=": return LessEqual(tokens[0], tokens[2], locn()) if tokens[1] == "=": return Equal(tokens[0], tokens[2], locn()) if tokens[1] == ">=": return GreaterEqual(tokens[0], tokens[2], locn()) if tokens[1] == ">": return Greater(tokens[0], tokens[2], locn()) if tokens[1] == "/=": return NotEqual(tokens[0], tokens[2], locn()) raise ParseFatalException(string, location, "unexpected relation operator")
def test_conditionally_unreachable_field_outgoing() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), LessEqual(Variable("F1"), Number(32))), Link(Field("F1"), FINAL, Greater(Variable("F1"), Number(32))), Link(Field("F2"), FINAL, Greater(Variable("F1"), Number(32))), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, } assert_message_model_error( structure, types, r"^" r'model: error: unreachable field "F2" in "P.M"\n' r"model: info: path 0 [(]F1 -> F2[)]:\n" r'model: info: unsatisfied "F1 <= 32"\n' r'model: info: unsatisfied "F1 > 32"', )