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_merge_message_error_name_conflict() -> None: m2_f2 = Field(ID("F2", Location((10, 5)))) m2 = UnprovenMessage( "P.M2", [Link(INITIAL, m2_f2), Link(m2_f2, FINAL)], {Field("F2"): MODULAR_INTEGER}, Location((15, 3)), ) m1_f1 = Field(ID("F1", Location((20, 8)))) m1_f1_f2 = Field(ID("F1_F2", Location((30, 5)))) m1 = UnprovenMessage( "P.M1", [Link(INITIAL, m1_f1), Link(m1_f1, m1_f1_f2), Link(m1_f1_f2, FINAL)], { Field("F1"): m2, Field("F1_F2"): MODULAR_INTEGER }, Location((2, 9)), ) assert_type_error( m1.merged(), r"^" r'<stdin>:30:5: model: error: name conflict for "F1_F2" in "P.M1"\n' r'<stdin>:15:3: model: info: when merging message "P.M2"\n' r'<stdin>:20:8: model: info: into field "F1"$', )
def __init__(self, identifier: StrID, generic_package: StrID, associations: Sequence[StrID] = None) -> None: self.identifier = ID(identifier) self.generic_package = ID(generic_package) self.associations = list(map(ID, associations or []))
def __init__(self, name: StrID, type_name: StrID, default: Expr = None) -> None: self.name = ID(name) self.type_name = ID(type_name) self.default = default
def __init__(self, name: StrID, generic_name: StrID, associations: Sequence[StrID] = None) -> None: self.name = ID(name) self.generic_name = ID(generic_name) self.associations = list(map(str, associations or []))
def __init__(self, identifier: StrID, type_identifier: StrID, type_: rty.Type = rty.Undefined()): super().__init__() self._identifier = ID(identifier) self._type_identifier = ID(type_identifier) self.type_ = type_
def create_sequence_instantiation( sequence_type: model.Sequence, prefix: str = "", flat: bool = False, ) -> Tuple[List[ContextItem], GenericPackageInstantiation]: element_type = sequence_type.element_type element_type_package = element_type.package.name sequence_context: List[ContextItem] = [] sequence_package: GenericPackageInstantiation if isinstance(element_type, model.Message): element_type_identifier = ID( element_type.identifier.flat if flat else prefix * element_type.identifier ) sequence_context = [ WithClause(prefix * const.MESSAGE_SEQUENCE_PACKAGE), *([] if flat else [WithClause(element_type_identifier)]), ] sequence_package = GenericPackageInstantiation( ID(sequence_type.identifier.flat if flat else prefix * sequence_type.identifier), prefix * const.MESSAGE_SEQUENCE_PACKAGE, [ element_type_identifier * "Context", element_type_identifier * "Initialize", element_type_identifier * "Take_Buffer", element_type_identifier * "Copy", element_type_identifier * "Has_Buffer", element_type_identifier * "Size", element_type_identifier * "Message_Last", element_type_identifier * "Initialized", element_type_identifier * "Structural_Valid_Message", ], ) elif isinstance(element_type, model.Scalar): element_type_identifier = prefix * element_type.identifier sequence_context = [ WithClause(prefix * const.SCALAR_SEQUENCE_PACKAGE), *( [WithClause(prefix * element_type_package)] if element_type_package != sequence_type.package else [] ), ] sequence_package = GenericPackageInstantiation( ID(sequence_type.identifier.flat if flat else prefix * sequence_type.identifier), prefix * const.SCALAR_SEQUENCE_PACKAGE, [ element_type_identifier, str(element_type.size), prefix * element_type_package * f"Valid_{element_type.name}", prefix * element_type_package * "To_Actual", prefix * element_type_package * "To_Base_Integer", ], ) else: assert False, 'unexpected element type "{type(element_type)}"' return (sequence_context, sequence_package)
def test_refinement_invalid_field_type() -> None: x = Field(ID("X", Location((20, 10)))) message = Message("P.M", [Link(INITIAL, x), Link(x, FINAL)], {x: MODULAR_INTEGER}) assert_type_error( Refinement("P", message, Field(ID("X", Location((33, 22)))), message), r'^<stdin>:33:22: model: error: invalid type of field "X" in refinement of "P.M"\n' r"<stdin>:20:10: model: info: expected field of type Opaque", )
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 test_in_ipv4_parsing_udp_in_ipv4_in_ethernet(ethernet_frame_value: pyrflx.MessageValue) -> None: with open(CAPTURED_DIR / "ethernet_ipv4_udp.raw", "rb") as file: msg_as_bytes: bytes = file.read() ethernet_frame_value.parse(msg_as_bytes) nested_ipv4 = ethernet_frame_value.get("Payload") assert isinstance(nested_ipv4, pyrflx.MessageValue) assert nested_ipv4.valid_message assert nested_ipv4.identifier == ID("IPv4") * "Packet" nested_udp = nested_ipv4.get("Payload") assert isinstance(nested_udp, pyrflx.MessageValue) assert nested_udp.valid_message assert nested_udp.identifier == ID("UDP") * "Datagram"
def test_attribute() -> None: assert isinstance(Size("X"), Attribute) assert isinstance(Length("X"), Attribute) assert isinstance(First("X"), Attribute) assert isinstance(Last("X"), Attribute) assert isinstance(Range("X"), Attribute) assert isinstance(Old("X"), Attribute) assert isinstance(Result("X"), Attribute) assert isinstance(Constrained("X"), Attribute) assert First("X") == First(Variable("X")) assert First("X") == First(ID("X")) assert First("X") == First(Variable(ID("X")))
def __init__( self, message: StrID, field: StrID, value: Expr, type_: rty.Type = rty.Undefined(), location: Location = None, ) -> None: super().__init__(message, type_, location) self.message = ID(message) self.field = ID(field) self.value = value
def test_ipv4_parsing_udp_in_ipv4_in_ethernet(frame: MessageValue) -> None: with open("tests/ethernet_ipv4_udp.raw", "rb") as file: msg_as_bytes: bytes = file.read() frame.parse(msg_as_bytes) nested_ipv4 = frame.get("Payload") assert isinstance(nested_ipv4, MessageValue) assert nested_ipv4.valid_message assert nested_ipv4.identifier == ID("IPv4.Packet") nested_udp = nested_ipv4.get("Payload") assert isinstance(nested_udp, MessageValue) assert nested_udp.valid_message assert nested_udp.identifier == ID("UDP.Datagram")
def test_name_conflict_types() -> None: assert_model_error( [ ModularInteger(ID("P::T"), Number(256), location=Location((10, 20))), RangeInteger( ID("P::T"), Number(1), Number(100), Number(8), location=Location((11, 30)) ), ], r"^" r'<stdin>:11:30: model: error: name conflict for type "P::T"\n' r'<stdin>:10:20: model: info: previous occurrence of "P::T"' r"$", )
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_field_locations() -> None: f2 = Field(ID("F2", Location((2, 2)))) f3 = Field(ID("F3", Location((3, 2)))) message = UnprovenMessage( "P.M", [Link(INITIAL, f2), Link(f2, f3), Link(f3, FINAL)], { Field("F2"): MODULAR_INTEGER, Field("F3"): MODULAR_INTEGER }, Location((17, 9)), ) assert message.fields == (f2, f3)
def __init__(self, identifier: StrID, type_: rty.Type = rty.Undefined(), location: Location = None): self.identifier = ID(identifier) self.type_ = type_ self.location = location
def test_named_aggregate_substituted() -> None: assert_equal( NamedAggregate(("First", First("X"))).substituted( lambda x: Number(42) if x == NamedAggregate(("First", First("X"))) else x ), Number(42), ) assert_equal( NamedAggregate(("First", First("X"))).substituted( lambda x: Number(42) if x == First("X") else x ), NamedAggregate(("First", Number(42))), ) assert_equal( NamedAggregate(("First", First("X"))).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else ( NamedAggregate(*[*x.elements, (ID("Last"), Last("Y"))]) if isinstance(x, NamedAggregate) else x ) ), NamedAggregate(("First", First("P_X")), ("Last", Last("P_Y"))), )
def __init__(self, identifiers: Sequence[StrID], type_name: StrID, default: Expr = None) -> None: self.identifiers = list(map(ID, identifiers)) self.type_name = ID(type_name) self.default = default
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_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 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 test_exclusive_with_length_invalid() -> None: f1 = Field(ID("F1", Location((98, 10)))) structure = [ Link(INITIAL, f1, length=Number(32)), Link(f1, FINAL, condition=Equal(Length("F1"), Number(32), Location((10, 2)))), Link(f1, Field("F2"), condition=Equal(Length("F1"), Number(32), Location((12, 4)))), Link(Field("F2"), FINAL), ] types = { Field("F1"): Opaque(), Field("F2"): RANGE_INTEGER, } assert_message_model_error( structure, types, r"^" r'<stdin>:98:10: model: error: conflicting conditions for field "F1"\n' r"<stdin>:10:2: model: info: condition 0 [(]F1 -> Final[)]: F1\'Length = 32\n" r"<stdin>:12:4: model: info: condition 1 [(]F1 -> F2[)]: F1\'Length = 32" r"$", )
def test_exclusive_conflict() -> None: f1 = Field(ID("F1", Location((8, 4)))) structure = [ Link(INITIAL, f1), Link(f1, FINAL, condition=Greater(Variable("F1"), Number(50), Location((10, 5)))), Link(f1, Field("F2"), condition=Less(Variable("F1"), Number(80), Location((11, 7)))), Link(Field("F2"), FINAL), ] types = { Field("F1"): RANGE_INTEGER, Field("F2"): RANGE_INTEGER, } assert_message_model_error( structure, types, r"^" r'<stdin>:8:4: model: error: conflicting conditions for field "F1"\n' r"<stdin>:10:5: model: info: condition 0 [(]F1 -> Final[)]: F1 > 50\n" r"<stdin>:11:7: model: info: condition 1 [(]F1 -> F2[)]: F1 < 80" r"$", )
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 fixture_icmp_checksum_message_first(icmp_message: model.Message) -> pyrflx.MessageValue: return pyrflx.MessageValue( icmp_message.copy( structure=[ model.Link( l.source, l.target, condition=expr.And(l.condition, expr.ValidChecksum("Checksum")), ) if l.target == model.FINAL else l for l in icmp_message.structure ], checksums={ ID("Checksum"): [ expr.ValueRange( expr.First("Message"), expr.Sub(expr.First("Checksum"), expr.Number(1)) ), expr.Size("Checksum"), expr.ValueRange( expr.Add(expr.Last("Checksum"), expr.Number(1)), expr.Last("Message") ), ] }, ) )
def _create_init_proc(self, slots: Sequence[NumberedSlotInfo]) -> UnitPart: proc = ProcedureSpecification( "Initialize", [OutParameter(["S"], "Slots"), Parameter(["M"], "Memory")]) return UnitPart( [ SubprogramDeclaration( proc, [Postcondition(Call("Initialized", [Variable("S")]))]), ], [ SubprogramBody( proc, declarations=[], statements=([ Assignment( "S" * self._slot_name(slot.slot_id), UnrestrictedAccess( Variable(ID(f"M.Slot_{slot.slot_id}"))), ) for slot in slots ] if slots else [NullStatement()]), aspects=[SparkMode(off=True)], ) ], )
def check_type(self, declaration_type: rty.Type, typify_variable: Callable[[Expr], Expr]) -> RecordFluxError: self.type_ = declaration_type expression = self.expression.substituted(typify_variable) assert isinstance(expression, Selected) self.expression = expression error = self.expression.prefix.check_type_instance(rty.Message) if error.errors: return error assert isinstance(self.expression.prefix.type_, rty.Message) error = RecordFluxError() for r in self.expression.prefix.type_.refinements: if ID(r.field) == self.expression.selector and r.sdu.is_compatible( declaration_type): break else: error.extend([ ( f'invalid renaming to "{self.identifier}"', Subsystem.MODEL, Severity.ERROR, self.location, ), ( f'refinement for message "{self.expression.prefix.type_.identifier}"' " would make operation legal", Subsystem.MODEL, Severity.INFO, self.location, ), ], ) return error + self.expression.check_type(rty.OPAQUE)
def assign(self, value: str, check: bool = True) -> None: prefixed_value = ( ID(value) if value.startswith(str(self._type.package)) or not self.__imported or self.__builtin else self._type.package * value) if Variable(prefixed_value) not in self.literals: raise KeyError(f"{value} is not a valid enum value") r = (And(*self._type.constraints( "__VALUE__", check, not self.__imported)).substituted( mapping={ **self.literals, **{ Variable("__VALUE__"): self._type.literals[prefixed_value.name] }, **{ Length("__VALUE__"): self._type.size }, }).simplified()) assert r == TRUE self._value = ( str(prefixed_value) if self.__imported and not self.__builtin else str(prefixed_value.name), self._type.literals[prefixed_value.name], )
def full_base_type_name(scalar_type: Scalar) -> ID: if scalar_type.package == BUILTINS_PACKAGE: return const.BUILTIN_TYPES_PACKAGE * scalar_type.name + "_Base" if isinstance(scalar_type, ModularInteger): return scalar_type.identifier return ID(f"{scalar_type.full_name}_Base")