def test_expr_variables(self) -> None: self.assertEqual( Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ).variables(), [Variable("Y"), Variable("X")], ) self.assertEqual( Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ).variables(), [Variable("Y"), Variable("X")], ) self.assertEqual( Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ).variables(), [Variable("Y"), Variable("X")], ) self.assertEqual( Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(1))) ).variables(), [Variable("Y"), Variable("X")], )
def test_bin_expr_converted(self) -> None: self.assertEqual( Less(Variable("X"), Number(1)).converted( lambda x: Name(x.name) if isinstance(x, Variable) else x ), Less(Name("X"), Number(1)), ) self.assertEqual( Sub(Variable("X"), Number(1)).converted( lambda x: Name("Y") if x == Sub(Variable("X"), Number(1)) else x ), Name("Y"), )
def test_expr_variables_duplicates(self) -> None: self.assertEqual( And(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Or(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Add(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Mul(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual(Sub(Variable("X"), Variable("X")).variables(), [Variable("X")]) self.assertEqual(Div(Variable("X"), Variable("X")).variables(), [Variable("X")]) self.assertEqual( Or( Greater(Variable("X"), Number(42)), And(TRUE, Less(Variable("X"), Number(1))) ).variables(), [Variable("X")], )
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 create_present_function() -> UnitPart: specification = FunctionSpecification( "Present", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [SubprogramDeclaration(specification)], [ ExpressionFunctionDeclaration( specification, AndThen( Call("Structural_Valid", [ Indexed(Variable("Ctx.Cursors"), Variable("Fld")) ]), Less( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "First"), Add( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Last"), Number(1), ), ), ), ) ], )
def enumeration_functions(enum: Enumeration) -> List[Subprogram]: common_precondition = And( Less(Value('Offset'), Number(8)), Equal( Length('Buffer'), Add( Div(Add(Size(enum.base_name), Value('Offset'), Number(-1)), Number(8)), Number(1)))) control_expression = LogCall( f'Convert_To_{enum.base_name} (Buffer, Offset)') validation_expression: Expr if enum.always_valid: validation_expression = Value('True') else: validation_cases: List[Tuple[Expr, Expr]] = [] validation_cases.extend( (value, Value('True')) for value in enum.literals.values()) validation_cases.append((Value('others'), Value('False'))) validation_expression = CaseExpression(control_expression, validation_cases) validation_function = ExpressionFunction( f'Valid_{enum.name}', 'Boolean', [('Buffer', 'Types.Bytes'), ('Offset', 'Natural')], validation_expression, [Precondition(common_precondition)]) function_name = f'Convert_To_{enum.name}' parameters = [('Buffer', 'Types.Bytes'), ('Offset', 'Natural')] precondition = Precondition( And(common_precondition, LogCall(f'Valid_{enum.name} (Buffer, Offset)'))) conversion_cases: List[Tuple[Expr, Expr]] = [] conversion_function: Subprogram if enum.always_valid: conversion_cases.extend((value, Aggregate(Value('True'), Value(key))) for key, value in enum.literals.items()) conversion_cases.append( (Value('others'), Aggregate(Value('False'), Value('Raw')))) conversion_function = Function( function_name, enum.name, parameters, [Declaration('Raw', enum.base_name, control_expression)], [ReturnStatement(CaseExpression(Value('Raw'), conversion_cases))], [precondition]) else: conversion_cases.extend( (value, Value(key)) for key, value in enum.literals.items()) conversion_cases.append( (Value('others'), LogCall(f'Unreachable_{enum.name}'))) conversion_function = ExpressionFunction( function_name, enum.name, parameters, CaseExpression(control_expression, conversion_cases), [precondition]) return [validation_function, conversion_function]
def create_valid_function() -> UnitPart: specification = FunctionSpecification( "Valid", "Boolean", [Parameter(["Ctx"], "Context"), Parameter(["Fld"], "Field")]) return UnitPart( [ SubprogramDeclaration( specification, [ Postcondition( If([( Result("Valid"), And( Call( "Structural_Valid", [Variable("Ctx"), Variable("Fld")], ), Call("Present", [Variable("Ctx"), Variable("Fld")]), ), )])), ], ) ], [ ExpressionFunctionDeclaration( specification, AndThen( Equal( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "State"), Variable("S_Valid"), ), Less( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "First"), Add( Selected( Indexed(Variable("Ctx.Cursors"), Variable("Fld")), "Last"), Number(1), ), ), ), ) ], )
def test_bin_expr_substituted() -> None: assert_equal( Less(Variable("X"), Number(1)).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else x ), Less(Variable("P_X"), Number(1)), ) assert_equal( Sub(Variable("X"), Number(1)).substituted( lambda x: Variable("Y") if x == Sub(Variable("X"), Number(1)) else x ), Variable("Y"), ) assert_equal( NotEqual(Variable("X"), Number(1)).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else (Equal(x.left, x.right) if isinstance(x, NotEqual) else x) ), Equal(Variable("P_X"), Number(1)), )
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_expr_contains() -> None: assert Variable("X") in Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ) assert Variable("Z") not in Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ) assert Less(Variable("X"), Number(42)) in Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42))) ) assert Less(Variable("Z"), Number(42)) not in Or( Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(1))) )
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_invalid_type_condition_modular_lower() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), condition=Less(Variable("F1"), Number(0))), Link(Field("F2"), FINAL), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER, } assert_message_model_error( structure, types, r"^" r'model: error: contradicting condition in "P.M"\n' r'model: info: on path: "F1"\n' r'model: info: unsatisfied "F1 >= 0"\n' r'model: info: unsatisfied "F1 < 0"', )
def test_exclusive_with_length_valid() -> None: structure = [ Link(INITIAL, Field("F1"), length=Number(32)), Link( Field("F1"), FINAL, condition=And(Equal(Length("F1"), Number(32)), Less(Variable("F1"), Number(50))), ), Link( Field("F1"), Field("F2"), condition=And(Equal(Length("F1"), Number(32)), Greater(Variable("F1"), Number(80))), ), Link(Field("F2"), FINAL), ] types = { Field("F1"): Opaque(), Field("F2"): MODULAR_INTEGER, } Message("P.M", structure, types)
def test_expr_contains(self) -> None: self.assertTrue( Variable("X") in Or(Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42)))) ) self.assertFalse( Variable("Z") in Or(Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42)))) ) self.assertTrue( Less(Variable("X"), Number(42)) in Or(Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(42)))) ) self.assertFalse( Less(Variable("Z"), Number(42)) in Or(Greater(Variable("Y"), Number(42)), And(TRUE, Less(Variable("X"), Number(1)))) )
def test_dot_graph_with_double_edge(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, Greater(Variable("X"), Number(100))), Link(Field("X"), FINAL, Less(Variable("X"), Number(50))), ], 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="(X < 50, 0, ⋆)", penwidth=0, style="", width=0]; X -> intermediate_1 [arrowhead=none]; intermediate_1 -> Final [minlen=1]; intermediate_2 [color="#6f6f6f", fontcolor="#6f6f6f", fontname="Fira Code", height=0, label="(X > 100, 0, ⋆)", penwidth=0, style="", width=0]; X -> intermediate_2 [arrowhead=none]; intermediate_2 -> Final [minlen=1]; Final [fillcolor="#6f6f6f", label="", shape=circle, width="0.5"]; } """ assert_graph(create_message_graph(m), expected, tmp_path)
def test_relation_variables(self) -> None: self.assertEqual(Less(Variable("X"), Name("Y")).variables(), [Variable("X")]) self.assertEqual( Less(Variable("X"), Variable("Y")).variables(), [Variable("X"), Variable("Y")] )
def test_less_than(self) -> None: self.assertEqual( Less(Number(1), Number(100)).z3expr(), z3.IntVal(1) < z3.IntVal(100))
def test_less_neg() -> None: assert -Less(Variable("X"), Number(1)) == GreaterEqual(Variable("X"), Number(1))
def test_greater_equal_neg(self) -> None: self.assertEqual(-GreaterEqual(Variable("X"), Number(1)), Less(Variable("X"), Number(1)))
def test_less_simplified(self) -> None: self.assertEqual( Less(Variable("X"), Add(Number(21), Number(21))).simplified(), Less(Variable("X"), Number(42)), )
def test_less_neg(self) -> None: self.assertEqual(-Less(Variable("X"), Number(1)), GreaterEqual(Variable("X"), Number(1)))
def test_relation_contains(self) -> None: self.assertTrue(Variable("X") in Less(Variable("X"), Number(42)))
def constraints(self, name: str, proof: bool = False) -> Expr: if proof: return And( Less(Variable(name), self.__modulus), GreaterEqual(Variable(name), Number(0)) ) return TRUE
def test_less_simplified() -> None: assert Less(Number(0), Number(1)).simplified() == TRUE assert Less(Number(1), Number(1)).simplified() == FALSE assert Less(Number(2), Number(1)).simplified() == FALSE
def test_greater_equal_neg() -> None: assert -GreaterEqual(Variable("X"), Number(1)) == Less(Variable("X"), Number(1))
def test_less_simplified(self) -> None: self.assertEqual( Less(Value('X'), Add(Number(21), Number(21))).simplified(), Less(Value('X'), Number(42)))
def test_and_contains(self) -> None: self.assertTrue(Variable("X") in And(TRUE, Less(Variable("X"), Number(42)))) self.assertFalse(Variable("Y") in And(TRUE, Less(Variable("X"), Number(42))))
def test_less_than() -> None: assert Less(Number(1), Number(100)).z3expr() == (z3.IntVal(1) < z3.IntVal(100))
def test_or_contains(self) -> None: self.assertTrue(Variable("X") in Or(Less(Variable("X"), Number(42)), TRUE)) self.assertFalse(Variable("Y") in Or(Less(Variable("X"), Number(42)), TRUE))