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_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 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 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_if() -> None: assert_equal( If( [ (Greater(Variable("a"), Number(5)), Number(1)), (Greater(Variable("b"), Number(100)), Number(10)), ], Number(100), ).z3expr(), z3.If( z3.Int("a") > z3.IntVal(5), z3.IntVal(1), z3.If(z3.Int("b") > z3.IntVal(100), z3.IntVal(10), z3.IntVal(100)), ), )
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 test_dot_graph_with_condition(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))), ], 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 > 100, 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_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_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_conditionally_unreachable_field_enum_first() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), Greater(First("F1"), First("Message"))), Link(Field("F2"), FINAL), ] types = { Field("F1"): ENUMERATION, Field("F2"): ENUMERATION, } assert_message_model_error( structure, types, r"^" r'model: error: unreachable field "F1" in "P.M"\n' r"model: info: path 0 [(]F1[)]:\n" r'model: info: unsatisfied "F1\'First = Message\'First"\n' r'model: info: unsatisfied "F1\'First > Message\'First"\n' r'model: error: unreachable field "F2" in "P.M"\n' r"model: info: path 0 [(]F1 -> F2[)]:\n" r'model: info: unsatisfied "F1\'First = Message\'First"\n' r'model: info: unsatisfied "F1\'First > Message\'First"\n' r'model: error: unreachable field "Final" in "P.M"\n' r"model: info: path 0 [(]F1 -> F2 -> Final[)]:\n" r'model: info: unsatisfied "F1\'First = Message\'First"\n' r'model: info: unsatisfied "F1\'First > Message\'First"', )
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_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_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 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_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"', )
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 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 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 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_upper() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), condition=Greater(Variable("F1"), Number(2**16 + 1))), 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 < 256"\n' r'model: info: unsatisfied "F1 > 65537"', )
def test_invalid_type_condition_range_high() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), condition=Greater(Variable("F1"), Number(200))), Link(Field("F2"), FINAL), ] types = { Field("F1"): RANGE_INTEGER, Field("F2"): RANGE_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 <= 100"\n' r'model: info: unsatisfied "F1 > 200"', )
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_greater() -> None: assert Greater(Number(100), Number(1)).z3expr() == (z3.IntVal(100) > z3.IntVal(1))
def test_greater_simplified(self) -> None: self.assertEqual( Greater(Value('X'), Add(Number(21), Number(21))).simplified(), Greater(Value('X'), Number(42)))
def test_greater(self) -> None: self.assertEqual( Greater(Number(100), Number(1)).z3expr(), z3.IntVal(100) > z3.IntVal(1))
def test_greater_simplified(self) -> None: self.assertEqual( Greater(Variable("X"), Add(Number(21), Number(21))).simplified(), Greater(Variable("X"), Number(42)), )
def test_greater_neg(self) -> None: self.assertEqual(-Greater(Variable("X"), Number(1)), LessEqual(Variable("X"), Number(1)))
def test_less_equal_neg(self) -> None: self.assertEqual(-LessEqual(Variable("X"), Number(1)), Greater(Variable("X"), Number(1)))