Enhancing the specification
JSXM allows to model machines with memory and complex processing functions that change the memory. The models are called Stream X-machines, or shortly SXMs.
Adding memory to the model
Let’s say that we want to count the number of times that the machine received the input on at the OFF state.
We first introduce a memory variable in the memory section of the specification:
<memory> <declaration> int counter; </declaration> <initial > counter = 0; </initial> <display > "On_Counter :" + counter </display> </memory>
The text within the <declaration>, <initial> and <display> XML tags is in-line Java code. You declare the variables you wish to use, you initialize them and you provide a way to be displayed. Notice that within the display you enter code that will render as a string.
Adding effects to processing functions
Let us now change the functions that handle the message on. We will add an <effect> tag with in-line Java code that will increase the counter memory variable.
<function name="SwitchOn" input="on" output="onOut" xsi:type="OutputFunction" > <effect> counter++; </effect> </function>
Animate the model and notice the value of the memory changing as you send the message on at the OFF state:
[INFO ] [....] ------------------------------------------------------------ [INFO ] [....] STATE : ON [INFO ] [....] MEMORY : On_Counter :3 [INFO ] [....] ------------------------------------------------------------
Adding preconditions (guards) to processing functions
We can add a condition under which the function will trigger with the specific input. The condition can depend on the memory variable. We will add an <precondition> tag with in-line Java code (evaluating to a boolean value) that will check if the counter memory variable is less than 2:
<function name="SwitchOn" input="on" output="onOut" xsi:type="OutputFunction" > <precondition> counter < 2 </precondition> <effect> counter++; </effect> </function>
NOTE: Within the in-line Java code you cannot use the symbols <, > and &. Instead you should type <, > and &.
Making the model complete
Animate the model and notice what will happen when you send the message on at the OFF state for the third time:
[INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@6b8c38 STATE : OFF [INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@6b8c38 MEMORY : On_Counter :3 [INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@6b8c38 INPUT : on() [INFO ] [...] Output : on_Error [INFO ] [...] ------------------------------------------------------------ [INFO ] [...] STATE : OFF [INFO ] [...] MEMORY : On_Counter :3 [INFO ] [...] ------------------------------------------------------------
The output you receive on_Error is different than any of the outputs and faults that we have declared in our model. The default error on_Error is generated by the animator when the input on cannot be handled by any processing function at the current state. This means that our specification is not complete. We need to add a processing function that will handle the on message when counter >= 2. We will name the new processing function BreakError:
<function name="BreakError" input="on" fault="breakError" xsi:type="FaultFunction" > <precondition> counter >= 2 </precondition> </function>
and we will a the new transition (BreakError) to the state diagram (from state OFF to state OFF):
<transitions> <transition from="OFF" function="SwitchOn" to="ON" /> <transition from="OFF" function="BreakError" to="OFF" /> <transition from="ON" function="SwitchOff" to="OFF" /> <transition from="ON" function="OnError" to="ON" /> <transition from="OFF" function="OffError" to="OFF" /> </transitions>
We also need to add the new fault output breakError in the faults section:
<faults> <fault name="onError" /> <fault name="breakError" /> <fault name="offError" /> </faults>
Animate the model and notice what will happen when you send the message on at the OFF state for the third time:
[INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@5152fc STATE : OFF [INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@5152fc MEMORY : On_Counter :2 [INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@5152fc INPUT : on() [INFO ] [...] ---org.jsxm.jsxmcore.types.CounterSXM@5152fc OUTPUT : breakError [INFO ] [...] --- [INFO ] [...] Output : breakError [INFO ] [...] ------------------------------------------------------------ [INFO ] [...] STATE : OFF [INFO ] [...] MEMORY : On_Counter :2 [INFO ] [...] ------------------------------------------------------------
Notice that the output onError is one of the declared faults in the specification. It is not the default animation error.
TIP: Avoid naming outputs or faults with the postfix _Error. Use some other name in order to clearly identify when a model is not complete.
Checking the Characterization and State Cover sets
Whenever you make any changes in the processing functions or the state transition diagram you have to check whether your characterization and state cover sets need changes too.
It is clear that we do not have to make any changes to the state cover since we did not add any new states.
However there is a problem with our characterization set since it was based on the sequence < on() >.
The input sequence < on() > at state OFF has now as possible outputs onOut via the SwitchOn function and BreakError via the BreakError function. At state ON it has as a result the fault onError via the OnError function, as it had before. So, in order to distinguish between the two states we need to probe the sequences <sequence><function name="SwitchOn" /></sequence>, <sequence><function name="BreakError" /></sequence> and <sequence><function name="OnError" /></sequence>.
The new characterization set is:
<characterization> <sequence> <function name="OnError" /> </sequence> <sequence> <function name="SwitchOn" /> </sequence> <sequence> <function name="BreakError" /> </sequence> </characterization>
Enhancing the specification with complex messages
Definition of Complex Inputs and Outputs
The input and the output messages can carry typed values as arguments. Let us add to the inputs a complex input setCounter(x) where x is an integer argument:
<inputs> ... <input name="setCounter"> <arg name="newCounterValue" type="xs:int" /> </input> ... </inputs>
There can be many arg elements within each input.
Similarly we can add a complex output:
<outputs> ... <output name="setCounterOut"> <result name="outValue" type="xs:int" /> </output> ... </outputs>
There can be many arg elements within each input and many result elements within each output.
JSXM supports most of the XSD types such as xs:int, xs:byte, xs:decimal, xs:string, xs:boolean and xs:char.
Handling of Complex Inputs and Outputs in functions
We will add a new processing function SetCounter that will process the complex input and will produce the complex output.
<function name="SetCounter" input="setCounter" output="setCounterOut" xsi:type="OutputFunction" > <effect> counter = setCounter.get_newCounterValue(); setCounterOut.outValue = counter; </effect> </function>
Notice the syntax for accessing the arguments of an input: inputName.get_argName(). inputName in this case is setCounter and argName is newCounterValue as defined in the definition of the input.
The syntax for setting the results of the outputs is easier: outputName.resultName. outputName in this case is setCounterOut and resultName is outValue as defined in the definition of the output. We add the transition with the new processing function in both states as a self-transition.
<transitions> ... <transition from="OFF" function="SetCounter" to="OFF" /> <transition from="ON" function="SetCounter" to="ON" /> ... </transitions>
Animate the model and notice what when you send the message setCounter an input window pops up in which you enter the number for the newCounterValue argument. Notice also the log screen of the animation. If you enter 2 then the input is setCounter(2) and the output is setCounterOut_2. Also notice how the memory variable is changing:
[INFO ] [22:55:47.492] Input : [INFO ] [22:55:47.494] Input argument: [newCounterValue] [INFO ] [22:55:47.494] Type: xs:int value :2 [INFO ] [22:55:47.496] ---org.jsxm.jsxmcore.types.CounterSXM@14adde7 [INFO ] [22:55:47.497] ---org.jsxm.jsxmcore.types.CounterSXM@14adde7 STATE : OFF [INFO ] [22:55:47.498] ---org.jsxm.jsxmcore.types.CounterSXM@14adde7 MEMORY : On_Counter :0 [INFO ] [22:55:47.498] ---org.jsxm.jsxmcore.types.CounterSXM@14adde7 INPUT : setCounter(2) [INFO ] [22:55:47.504] ---org.jsxm.jsxmcore.types.CounterSXM@14adde7 OUTPUT : setCounterOut_2 [INFO ] [22:55:47.504] --- [INFO ] [22:55:47.505] Output : setCounterOut_2 [INFO ] [22:55:47.505] ------------------------------------------------------------ [INFO ] [22:55:47.506] STATE : OFF [INFO ] [22:55:47.506] MEMORY : On_Counter :2 [INFO ] [22:55:47.506] ------------------------------------------------------------ [INFO ] [22:55:47.507] Input :
Generating Test cases for models with complex inputs
If we try to generate test cases for our new model we will get an exception from the tool:
Exception in thread "pool-4-thread-1" org.jsxm.jsxmcore.runtimeexceptions.FunctionPreconditionEffectException: Precondition/effect of function: SetCounter Possibly the function has no input generator!
When a processing function, like SetCounter, receives arguments we need to define an input generator that will create inputs for the function:
<testinputgeneration> <inputgenerator function="SetCounter"> setCounter.set_newCounterValue(0); </inputgenerator> </testinputgeneration>
Input generators are usually quite simple to write, especially if the function has no precondition. In this case we set the argument newCounterValue to the setCounter input to be equal to 0.
Of course to execute the JUnit test we need to make changes in the Adapter and in our implementation since our model has changed quite a lot. I leave this to you.
GOOD LUCK!