def test_parsing_model(tmp_path_factory: TempPathFactory, model: Model) -> None: tmp_path = tmp_path_factory.mktemp(test_parsing_model.__name__) model.write_specification_files(tmp_path) parser = Parser() parser.parse(tmp_path / "test.rflx") parsed_model = parser.create_model() assert parsed_model.types == model.types assert parsed_model == model
def test_write_specification_files_missing_deps(tmp_path: Path) -> None: s = ModularInteger("P::S", Number(65536)) t = ModularInteger("P::T", Number(256)) v = mty.Sequence("P::V", element_type=t) m = Message("P::M", [Link(INITIAL, Field("Foo")), Link(Field("Foo"), FINAL)], {Field("Foo"): t}) Model([s, v, m]).write_specification_files(tmp_path) expected_path = tmp_path / Path("p.rflx") assert list(tmp_path.glob("*.rflx")) == [expected_path] assert expected_path.read_text() == textwrap.dedent( """\ package P is type S is mod 65536; type T is mod 256; type V is sequence of P::T; type M is message Foo : P::T; end message; end P;""" )
def test_write_specification_file_multiple_packages_missing_deps(tmp_path: Path) -> None: t = ModularInteger("P::T", Number(256)) u = mty.Sequence("R::U", element_type=t) u1 = mty.Sequence("Q::U1", element_type=t) v = ModularInteger("R::V", Number(65536)) links = [ Link(INITIAL, Field("Victor")), Link(Field("Victor"), Field("Uniform")), Link(Field("Uniform"), FINAL), ] fields = {Field("Victor"): v, Field("Uniform"): u} m = Message("R::M", links, fields) Model([u1, m, u, v]).write_specification_files(tmp_path) p_path, q_path, r_path = (tmp_path / Path(pkg + ".rflx") for pkg in ("p", "q", "r")) assert set(tmp_path.glob("*.rflx")) == {p_path, q_path, r_path} assert p_path.read_text() == textwrap.dedent( """\ package P is type T is mod 256; end P;""" ) assert q_path.read_text() == textwrap.dedent( """\ with P; package Q is type U1 is sequence of P::T; end Q;""" ) assert r_path.read_text() == textwrap.dedent( """\ with P; package R is type V is mod 65536; type U is sequence of P::T; type M is message Victor : R::V then Uniform with Size => Message'Last - Victor'Last; Uniform : R::U; end message; end R;""" )
def test_name_conflict_sessions() -> None: s1 = copy(models.SESSION) s1.location = Location((10, 20)) s2 = copy(models.SESSION) s2.location = Location((10, 30)) with pytest.raises( RecordFluxError, match=( r"^" r'<stdin>:10:30: model: error: name conflict for session "P::S"\n' r'<stdin>:10:20: model: info: previous occurrence of "P::S"' r"$" ), ): Model([], [s1, s2])
def create_model(self) -> Model: error = RecordFluxError() for specification in self.__specifications: if specification.package.identifier in self.__evaluated_specifications: continue self.__evaluated_specifications.add( specification.package.identifier) try: self.__evaluate_specification(specification) except RecordFluxError as e: error.extend(e) try: result = Model(list(self.__types.values())) except RecordFluxError as e: error.extend(e) error.propagate() return result
def _create_model(self, files: List[Path], skip_model_verification: bool, split_disjunctions: bool) -> Model: for f in files: if not f.is_file(): raise ValidationError(f'specification file not found: "{f}"') parser = Parser(skip_model_verification) parser.parse(*files) model = parser.create_model() if split_disjunctions: messages: Dict[ID, Message] = {} for t in model.types: if isinstance(t, Message): messages[t.identifier] = self._expand_message_links( t, messages) model = Model( [self._replace_messages(t, messages) for t in model.types]) return model
def models(draw: Draw) -> Model: types_: ty.List[Type] = [] def append_types(message: Message) -> None: types_.append(message) for t in message.types.values(): if isinstance(t, Opaque): continue types_.append(t) if isinstance(t, Sequence): if isinstance(t.element_type, Message): append_types(t.element_type) else: types_.append(t.element_type) refinement = draw(refinements(unique_qualified_identifiers())) types_.append(refinement) for m in [refinement.sdu, refinement.pdu]: append_types(m) for t in [*INTERNAL_TYPES.values(), *BUILTIN_TYPES.values()]: types_.append(t) return Model(list(reversed(types_)))
Field, Link, Message, 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},
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 assert_model_error(types: Sequence[Type], regex: str) -> None: with pytest.raises(RecordFluxError, match=regex): Model([*BUILTIN_TYPES.values(), *types])
def test_write_specification_files_line_too_long(tmp_path: Path) -> None: t = ModularInteger("P::" + "T" * 120, Number(256)) Model([t]).write_specification_files(tmp_path) expected_path = tmp_path / Path("p.rflx") assert list(tmp_path.glob("*.rflx")) == [expected_path] assert expected_path.read_text().startswith("-- style: disable = line-length\n\npackage P is")
def test_init_introduce_type_dependencies(types: List[Type], model: Model) -> None: assert Model(types).types == model.types
INITIAL, Array, 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))),
def test_unexpected_type() -> None: class TestType(Type): pass with pytest.raises(AssertionError, match='unexpected type "TestType"'): Generator().generate(Model([TestType("P.T")]))