SCXML-285 Fix: Before executing invoke handlers after a macrostep all internal events... master
authorAte Douma <ate@apache.org>
Wed, 17 Oct 2018 22:52:51 +0000 (00:52 +0200)
committerAte Douma <ate@apache.org>
Wed, 17 Oct 2018 22:52:51 +0000 (00:52 +0200)
src/changes/changes.xml
src/main/java/org/apache/commons/scxml2/semantics/SCXMLSemanticsImpl.java
src/test/java/org/apache/commons/scxml2/invoke/InvokeTest.java
src/test/java/org/apache/commons/scxml2/invoke/invoker-05.xml [new file with mode: 0644]

index 7f7a61a..94c7220 100644 (file)
     <release version="2.0" date="In Git master"
       description="Latest unreleased code">
 
+      <action dev="ate" type="fix" issue="SCXML-285">
+        [18-10-2018] Before executing invoke handlers after a macrostep all internal events must have been processed
+      </action>
+
       <action dev="woonsan" type="update" issue="SCXML-284" due-to="Allon Mureinik">
         [10-10-2018] Clear up exception handling in tests
       </action>
index 5fb5f2a..423f13f 100644 (file)
@@ -316,15 +316,13 @@ public class SCXMLSemanticsImpl implements SCXMLSemantics {
                             step = new Step(event);
                             selectTransitions(exctx, step);
                         }
+                    } else {
+                        macroStepDone = true;
                     }
                 }
-                if (step.getTransitList().isEmpty()) {
-                    macroStepDone = true;
-                }
-                else {
+                if (!step.getTransitList().isEmpty()) {
                     microStep(exctx, step, statesToInvoke);
                 }
-
             } while (exctx.isRunning() && !macroStepDone);
 
             if (exctx.isRunning() && !statesToInvoke.isEmpty()) {
index fcf5c56..7f2be71 100644 (file)
@@ -72,5 +72,18 @@ public class InvokeTest {
         SCXMLTestHelper.fireEvent(exec, "s1.next");
         SCXMLTestHelper.fireEvent(exec, "state1.next");
     }
+
+    @Test
+    public void testExecuteInvokeAfterAllInternalEventsAreProcessed() throws Exception {
+        SCXML scxml = SCXMLReader.read(SCXMLTestHelper.getResource("org/apache/commons/scxml2/invoke/invoker-05.xml"));
+        SCXMLExecutor exec = new SCXMLExecutor(null, new SimpleDispatcher(), new SimpleErrorReporter());
+        exec.setStateMachine(scxml);
+        exec.registerInvokerClass("scxml", SimpleSCXMLInvoker.class);
+        exec.go();
+        while (exec.isRunning()) {
+            exec.triggerEvents();
+        }
+        Assertions.assertEquals("success", exec.getStatus().getStates().iterator().next().getId());
+    }
 }
 
diff --git a/src/test/java/org/apache/commons/scxml2/invoke/invoker-05.xml b/src/test/java/org/apache/commons/scxml2/invoke/invoker-05.xml
new file mode 100644 (file)
index 0000000..99dc87b
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<!-- Test for REC-scxml-20150901, 3.13: Invoke handlers must be executed *after* completing a macrostrep,
+     for which first all internal events must have been processed.
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" datamodel="jexl">
+    <datamodel>
+        <data id="var1" expr="0"/>
+    </datamodel>
+    <state id="start">
+        <onentry>
+            <!-- dummy internal event to force the next before.invoke to be not the first -->
+            <raise event="dummy"/>
+            <!-- Trigger an update of the param value: must be evaluated *before* executing invoke.
+                 This is a second internal event, to validate *all* internal events are processed first
+            -->
+            <raise event="before.invoke"/>
+        </onentry>
+        <invoke>
+            <param name="var1" expr="var1"/>
+            <content>
+                <scxml version="1.0" datamodel="jexl">
+                    <datamodel>
+                        <data id="var1" expr="0"/>
+                    </datamodel>
+                    <state id="invoked">
+                        <transition cond="var1==1" target="done">
+                            <!-- param val1 was updated through internal event before executing invoke -->
+                            <send target="#_parent" event="success"/>
+                        </transition>
+                        <transition target="done">
+                            <send target="#_parent" event="fail"/>
+                        </transition>
+                    </state>
+                    <final id="done"/>
+                </scxml>
+            </content>
+        </invoke>
+        <transition event="before.invoke">
+            <!-- all internal events like this one must be processed before completing the macrostep, only thereafter
+                 the invoke may be executed
+            -->
+            <assign location="var1" expr="1"/>
+        </transition>
+        <transition event="fail" target="fail"/>
+        <transition event="success" target="success"/>
+    </state>
+    <final id="fail"/>
+    <final id="success"/>
+</scxml>