Jelajahi Sumber

Handle no signature case for model transformations

Yentl Van Tendeloo 7 tahun lalu
induk
melakukan
8aa8fd0833
4 mengubah file dengan 176 tambahan dan 27 penghapusan
  1. 136 3
      unit/test_all.py
  2. 7 0
      wrappers/classes/modelverse.xml
  3. 3 5
      wrappers/modelverse.py
  4. 30 19
      wrappers/modelverse_SCCD.py

+ 136 - 3
unit/test_all.py

@@ -4043,11 +4043,144 @@ class TestModelverse(unittest.TestCase):
         except ModelExists:
             assert sig == transformation_signature("users/user/test/c")
 
+    def test_op_transformation_add_MT(self):
+        # Add models for transformation
+        model_add("users/user/test/A", "formalisms/SimpleClassDiagrams", """
+            SimpleAttribute String {}
+            Class A {
+                name = "A"
+                name : String
+            }
+            """)
+        model_add("users/user/test/B", "formalisms/SimpleClassDiagrams", """
+            SimpleAttribute String {}
+            Class B {
+                name = "B"
+                name : String
+            }
+            """)
+        model_add("users/user/test/a", "users/user/test/A", "A {}")
+        model_add("users/user/test/b", "users/user/test/B", "B {}")
+
+        default_function = "Composite c{}"
+
+        # Add a transformation with normal signature
+        transformation_add_MT({"MODEL_A": "users/user/test/A"}, {"MODEL_B": "users/user/test/B"}, "users/user/test/c", default_function)
+
+        # Add a transformation with normal signature and merged metamodel changes
+        def operation(model):
+            # Check if both are present
+            lst = element_list_nice(model)
+            assert len(lst) == 6
+            assert {"__id": "MODEL_A/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A", "__type": "Class", "name": "A", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A_name", "__type": "AttributeLink", "__source": "MODEL_A/A", "__target": "MODEL_A/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B", "__type": "Class", "name": "B", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B_name", "__type": "AttributeLink", "__source": "MODEL_B/B", "__target": "MODEL_B/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+
+            # Do minor merge operation
+            instantiate(model, "Association", edge=("MODEL_A/A", "MODEL_B/B"), ID="trace")
+
+            # Check again
+            lst = element_list_nice(model)
+            assert len(lst) == 7
+            assert {"__id": "MODEL_A/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A", "__type": "Class", "name": "A", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A_name", "__type": "AttributeLink", "__source": "MODEL_A/A", "__target": "MODEL_A/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B", "__type": "Class", "name": "B", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B_name", "__type": "AttributeLink", "__source": "MODEL_B/B", "__target": "MODEL_B/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+            assert {"__id": "trace", "__type": "Association", "__source": "MODEL_A/A", "__target": "MODEL_B/B", "name": None, "source_lower_cardinality": None, "source_upper_cardinality": None, "target_lower_cardinality": None, "target_upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}, "lower_cardinality": None, "upper_cardinality": None} in lst
+
+        transformation_add_MT({"MODEL_A": "users/user/test/A"}, {"MODEL_B": "users/user/test/B"}, "users/user/test/d", default_function, operation)
+        
+        def operation(model):
+            # Check if both are present
+            lst = element_list_nice(model)
+            assert len(lst) == 6
+            assert {"__id": "MODEL_A/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A", "__type": "Class", "name": "A", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_A/A_name", "__type": "AttributeLink", "__source": "MODEL_A/A", "__target": "MODEL_A/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/String", "__type": "SimpleAttribute", "name": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B", "__type": "Class", "name": "B", "lower_cardinality": None, "upper_cardinality": None, "abstract": None, "constraint": {"AL": ""}} in lst
+            assert {"__id": "MODEL_B/B_name", "__type": "AttributeLink", "__source": "MODEL_B/B", "__target": "MODEL_B/String", "name": "name", "optional": False, "constraint": {"AL": ""}} in lst
+
+        transformation_add_MT({"MODEL_A": "users/user/test/A", "MODEL_B": "users/user/test/B"}, {"MODEL_B": "users/user/test/B"}, "users/user/test/e", default_function, operation)
+
+        try:
+            transformation_add_MT({"MODEL_A": "users/user/test/A", "MODEL_B": "users/user/test/B"}, {"MODEL_B": "users/user/test/A"}, "users/user/test/f", default_function)
+            self.fail()
+        except SignatureMismatch:
+            pass
+
+        # Add a transformation with empty signature
+        try:
+            transformation_add_MT({}, {}, "users/user/test/g", default_function)
+            self.fail()
+        except EmptySignature:
+            assert "g" not in model_list("users/user/test")
+
+        # Add a transformation with empty signature and a callback
+        try:
+            def operation(model):
+                pass
+            transformation_add_MT({}, {}, "users/user/test/h", default_function, operation)
+            self.fail()
+        except CallbackOnEmptySignature:
+            assert "h" not in model_list("users/user/test")
+
+        # Add transformation with unknown metamodel in signature (input)
+        try:
+            transformation_add_MT({"MODEL_A": "adbdsf"}, {"MODEL_B": "users/user/test/A"}, "users/user/test/i", default_function)
+            self.fail()
+        except UnknownModel:
+            assert "i" not in model_list("users/user/test")
+
+        # Add transformation with unknown metamodel in signature (output)
+        try:
+            transformation_add_MT({"MODEL_A": "users/user/test/A"}, {"MODEL_B": "adfad"}, "users/user/test/j", default_function)
+            self.fail()
+        except UnknownModel:
+            assert "j" not in model_list("users/user/test")
+
+        # Add transformation with unreadable metamodel in signature (input)
+        try:
+            transformation_add_MT({"MODEL_A": "administration/core"}, {"MODEL_B": "users/user/test/B"}, "users/user/test/k", default_function)
+            self.fail()
+        except ReadPermissionDenied:
+            assert "k" not in model_list("users/user/test")
+
+        # Add transformation with unreadable metamodel in signature (output)
+        try:
+            transformation_add_MT({"MODEL_A": "users/user/test/A"}, {"MODEL_B": "administration/core"}, "users/user/test/l", default_function)
+            self.fail()
+        except ReadPermissionDenied:
+            assert "l" not in model_list("users/user/test")
+
+        # Try to use a non-RAMifiable metamodel in input
+        try:
+            transformation_add_MT({"MODEL_A": "users/user/test/a"}, {}, "users/user/test/m", default_function)
+            self.fail()
+        except UnknownM3:
+            assert "m" not in model_list("users/user/test")
+
+        # Try to use a non-RAMifiable metamodel in output
+        try:
+            transformation_add_MT({}, {"MODEL_A": "users/user/test/a"}, "users/user/test/n", default_function)
+            self.fail()
+        except UnknownM3:
+            assert "n" not in model_list("users/user/test")
+
+        # Try to create activity that already exists
+        sig = transformation_signature("users/user/test/c")
+        try:
+            transformation_add_MT({}, {}, "users/user/test/c", default_function)
+        except ModelExists:
+            assert sig == transformation_signature("users/user/test/c")
+
     """
     def test_op_model_render(self):
-    def test_op_transformation_add_MT(self):
-    def test_op_transformation_add_AL(self):
-    def test_op_transformation_add_MANUAL(self):
     def test_op_transformation_execute_MT(self):
     def test_op_transformation_execute_AL(self):
     def test_op_transformation_execute_MANUAL(self):

+ 7 - 0
wrappers/classes/modelverse.xml

@@ -1532,6 +1532,13 @@
                         </raise>
                     </transition>
 
+                    <transition cond="self.expect_response_partial('Model transformation needs at least one formalism in its input or output signature!', pop=True)" target="../wait_for_action/history">
+                        <raise event="exception">
+                            <parameter expr="'EmptySignature'"/>
+                            <parameter expr="'Model transformation activity cannot have an empty signature!'"/>
+                        </raise>
+                    </transition>
+
                     <transition cond="self.expect_response_partial('Execute permission denied to: ', pop=False)" target="../wait_for_action/history">
                         <raise event="exception">
                             <parameter expr="'ExecutePermissionDenied'"/>

+ 3 - 5
wrappers/modelverse.py

@@ -27,6 +27,9 @@ class CallbackOnEmptySignature(ModelverseException):
 class NotAModel(ModelverseException):
     pass
 
+class EmptySignature(ModelverseException):
+    pass
+
 class ManualActivityRequiresIO(ModelverseException):
     pass
 
@@ -320,7 +323,6 @@ def transformation_between(sources, targets):
     return OUTPUT()
 
 def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=None):
-
     if len(source_metamodels) + len(target_metamodels) == 0:
         if callback is not None:
             raise CallbackOnEmptySignature()
@@ -337,15 +339,12 @@ def transformation_add_MT(source_metamodels, target_metamodels, operation_name,
         return OUTPUT()
 
 def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=None):
-
     if len(source_metamodels) + len(target_metamodels) == 0:
         if callback is not None:
             raise CallbackOnEmptySignature()
 
     INPUT("transformation_add_AL", [source_metamodels, target_metamodels, operation_name, code, True])
-    print("Wait for model")
     model = OUTPUT()
-    print("Model: " + str(model))
 
     if model is None:
         return OUTPUT()
@@ -356,7 +355,6 @@ def transformation_add_AL(source_metamodels, target_metamodels, operation_name,
         return OUTPUT()
 
 def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=None):
-
     if len(source_metamodels) + len(target_metamodels) == 0:
         if callback is not None:
             raise CallbackOnEmptySignature()

+ 30 - 19
wrappers/modelverse_SCCD.py

@@ -1893,6 +1893,11 @@ class Modelverse(RuntimeClassBase):
         _initialized_behaviour_operations_31.setTrigger(None)
         _initialized_behaviour_operations_31.setGuard(self._initialized_behaviour_operations_31_guard)
         self.states["/initialized/behaviour/operations"].addTransition(_initialized_behaviour_operations_31)
+        _initialized_behaviour_operations_32 = Transition(self, self.states["/initialized/behaviour/operations"], [self.states["/initialized/behaviour/wait_for_action/history"]])
+        _initialized_behaviour_operations_32.setAction(self._initialized_behaviour_operations_32_exec)
+        _initialized_behaviour_operations_32.setTrigger(None)
+        _initialized_behaviour_operations_32.setGuard(self._initialized_behaviour_operations_32_guard)
+        self.states["/initialized/behaviour/operations"].addTransition(_initialized_behaviour_operations_32)
         
         # transition /initialized/behaviour/operations/store_on_scripted/transformation_add
         _initialized_behaviour_operations_store_on_scripted_transformation_add_0 = Transition(self, self.states["/initialized/behaviour/operations/store_on_scripted/transformation_add"], [self.states["/initialized/behaviour/wait_for_action/megamodelling"]])
@@ -2410,65 +2415,71 @@ class Modelverse(RuntimeClassBase):
         return self.expect_response_partial('Not a SimpleClassDiagrams model!', pop=True)
     
     def _initialized_behaviour_operations_22_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['ExecutePermissionDenied', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['EmptySignature', 'Model transformation activity cannot have an empty signature!']))
     
     def _initialized_behaviour_operations_22_guard(self, parameters):
-        return self.expect_response_partial('Execute permission denied to: ', pop=False)
+        return self.expect_response_partial('Model transformation needs at least one formalism in its input or output signature!', pop=True)
     
     def _initialized_behaviour_operations_23_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['GroupPermissionDenied', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['ExecutePermissionDenied', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_23_guard(self, parameters):
-        return self.expect_response_partial('Group permission denied to: ', pop=False)
+        return self.expect_response_partial('Execute permission denied to: ', pop=False)
     
     def _initialized_behaviour_operations_24_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['UserPermissionDenied', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['GroupPermissionDenied', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_24_guard(self, parameters):
-        return self.expect_response_partial('User permission denied to: ', pop=False)
+        return self.expect_response_partial('Group permission denied to: ', pop=False)
     
     def _initialized_behaviour_operations_25_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['UserNotInGroup', '']))
+        self.raiseInternalEvent(Event("exception", None, ['UserPermissionDenied', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_25_guard(self, parameters):
-        return self.expect_response_partial('User is not a member of the group!', pop=True)
+        return self.expect_response_partial('User permission denied to: ', pop=False)
     
     def _initialized_behaviour_operations_26_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['AdminPermissionDenied', 'Admin permissions are required for this operation!']))
+        self.raiseInternalEvent(Event("exception", None, ['UserNotInGroup', '']))
     
     def _initialized_behaviour_operations_26_guard(self, parameters):
-        return self.expect_response_partial('Admin permission denied', pop=True)
+        return self.expect_response_partial('User is not a member of the group!', pop=True)
     
     def _initialized_behaviour_operations_27_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['InterfaceMismatch', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['AdminPermissionDenied', 'Admin permissions are required for this operation!']))
     
     def _initialized_behaviour_operations_27_guard(self, parameters):
-        return self.expect_response_partial('Incorrect format: ', pop=False)
+        return self.expect_response_partial('Admin permission denied', pop=True)
     
     def _initialized_behaviour_operations_28_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['UnknownElement', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['InterfaceMismatch', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_28_guard(self, parameters):
-        return self.expect_response_partial('Element not found: ', pop=False)
+        return self.expect_response_partial('Incorrect format: ', pop=False)
     
     def _initialized_behaviour_operations_29_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['UnknownModel', self.split_response(self.responses.pop(0))[0]]))
+        self.raiseInternalEvent(Event("exception", None, ['UnknownElement', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_29_guard(self, parameters):
-        return self.expect_response_partial('Model not found: ', pop=False)
+        return self.expect_response_partial('Element not found: ', pop=False)
     
     def _initialized_behaviour_operations_30_exec(self, parameters):
-        self.raiseInternalEvent(Event("exception", None, ['UnknownMetamodellingHierarchy', 'Metamodelling hierarchy could not be resolved or automatically inferred: there is no typing relation between your specified model and metamodel (%s)' % self.responses.pop(0)]))
+        self.raiseInternalEvent(Event("exception", None, ['UnknownModel', self.split_response(self.responses.pop(0))[0]]))
     
     def _initialized_behaviour_operations_30_guard(self, parameters):
-        return self.expect_response_partial('Conformance hierarchy unknown for: ', pop=False)
+        return self.expect_response_partial('Model not found: ', pop=False)
     
     def _initialized_behaviour_operations_31_exec(self, parameters):
+        self.raiseInternalEvent(Event("exception", None, ['UnknownMetamodellingHierarchy', 'Metamodelling hierarchy could not be resolved or automatically inferred: there is no typing relation between your specified model and metamodel (%s)' % self.responses.pop(0)]))
+    
+    def _initialized_behaviour_operations_31_guard(self, parameters):
+        return self.expect_response_partial('Conformance hierarchy unknown for: ', pop=False)
+    
+    def _initialized_behaviour_operations_32_exec(self, parameters):
         print("Unknown Error: " + self.responses[0])
         pass
         self.raiseInternalEvent(Event("exception", None, ['UnknownError', 'Error: %s' % self.responses.pop(0)]))
     
-    def _initialized_behaviour_operations_31_guard(self, parameters):
+    def _initialized_behaviour_operations_32_guard(self, parameters):
         return self.expect_response_partial('', pop=False)
     
     def _initialized_behaviour_operations_store_on_scripted_transformation_add_0_exec(self, parameters):