diff --git a/.gitignore b/.gitignore index fe8ea39..3dc4d04 100644 --- a/.gitignore +++ b/.gitignore @@ -134,4 +134,4 @@ dmypy.json # JetBrains IDE .idea/ -.vscode/ \ No newline at end of file +.vscode/ diff --git a/bpmnconstraints/parser/bpmn_parser.py b/bpmnconstraints/parser/bpmn_parser.py index 3bf146c..a61c6ee 100644 --- a/bpmnconstraints/parser/bpmn_parser.py +++ b/bpmnconstraints/parser/bpmn_parser.py @@ -59,12 +59,29 @@ def run(self): self.__mark_gateway_elements() if self.transitivity: self.__add_transitivity() + self.validate_splitting_and_joining_gateway_cases() return self.sequence except Exception: logging.warning( "\nCould not execute model. Make sure that model is:\n1. Formatted correctly.\n2. File ends with .xml or .json." ) + def validate_splitting_and_joining_gateway_cases(self): + """Update 'is start' and 'is end' attributes of cfo based on splitting/joining gateways. + Otherwise, the parser interprets the gateways as start/end events instead of the activities. + """ + + item_indices = {item["name"]: index for index, item in enumerate(self.sequence)} + for cfo in self.sequence: + if cfo["is start"] and cfo["name"] == "XOR": + cfo["is start"] = False + for successor in cfo["successor"]: + self.sequence[item_indices[successor["name"]]]["is start"] = True + if cfo["is end"] and cfo["name"] in GATEWAY_NAMES: + cfo["is end"] = False + for predecessor in cfo["predecessor"]: + self.sequence[item_indices[predecessor["name"]]]["is end"] = True + def __mark_gateway_elements(self): for cfo in self.sequence: predecessors = cfo.get("predecessor") diff --git a/tests/test_end_constraints.py b/tests/test_end_constraints.py index 963c20f..8b80d84 100644 --- a/tests/test_end_constraints.py +++ b/tests/test_end_constraints.py @@ -1,7 +1,9 @@ -from test_utils import init_test_setup_for_compiler +from test_utils import init_test_setup_for_compiler, init_test_setup_for_parser from file_constants import ( MULTIPLE_ENDINGS_DIAGRAM, LINEAR_SEQUENCE_DIAGRAM_WITHOUT_START_AND_END, + XOR_GATEWAY_SEQUENCE_DIAGRAM, + LINEAR_MERMAID_GRAPH, ) @@ -15,6 +17,26 @@ def test_end_constraint_is_generated_when_multiple_endings(): assert all(constraint in res for constraint in expected_ending_constraints) +def test_end_constraint_is_generated_with_linear_parser(): + res = init_test_setup_for_parser(LINEAR_MERMAID_GRAPH) + assert res[-1]["is end"] == True + + def test_end_constraint_is_generated_without_explicit_end_event(): res = init_test_setup_for_compiler(LINEAR_SEQUENCE_DIAGRAM_WITHOUT_START_AND_END) assert "End[second element]" in res + + +def test_end_constraint_is_generated_when_multiple_endings(): + res = init_test_setup_for_parser(XOR_GATEWAY_SEQUENCE_DIAGRAM) + + assert res[-1]["is end"] and res[-2]["is end"] and not res[3]["is end"] + + +def test_end_constraint_is_generated_when_xor_gateway(): + res = init_test_setup_for_compiler(XOR_GATEWAY_SEQUENCE_DIAGRAM) + expected_ending_constraints = [ + "End[activity four]", + "End[activity five]", + ] + assert all(constraint in res for constraint in expected_ending_constraints) diff --git a/tests/test_init_constraints.py b/tests/test_init_constraints.py index e4d3c38..247776b 100644 --- a/tests/test_init_constraints.py +++ b/tests/test_init_constraints.py @@ -1,7 +1,8 @@ -from test_utils import init_test_setup_for_compiler +from test_utils import init_test_setup_for_compiler, init_test_setup_for_parser from file_constants import ( LINEAR_SEQUENCE_DIAGRAM_WITHOUT_START_AND_END, MULTIPLE_STARTS_DIAGRAM, + XOR_GATEWAY_SEQUENCE_DIAGRAM, ) @@ -12,4 +13,19 @@ def test_init_constraint_is_generated_without_explicit_start_event(): def test_that_each_start_has_init_constraint(): res = init_test_setup_for_compiler(MULTIPLE_STARTS_DIAGRAM) - assert ["Init[path one]", "Init[path two]"] == res + expected_init_constraints = ["Init[path one]", "Init[path two]"] + assert all(constraint in res for constraint in expected_init_constraints) + + +def test_missing_init_constraints_for_XOR_gate_parser(): + res = init_test_setup_for_parser(XOR_GATEWAY_SEQUENCE_DIAGRAM) + assert res[4]["is start"] and res[5]["is start"] and not res[0]["is start"] + + +def test_missing_init_constraints_for_XOR_gate(): + res = init_test_setup_for_compiler(XOR_GATEWAY_SEQUENCE_DIAGRAM) + expected_init_constraints = [ + "Init[activity one]", + "Init[activity two]", + ] + assert all(constraint in res for constraint in expected_init_constraints)