Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Commit

Permalink
Store incoming transitions #24
Browse files Browse the repository at this point in the history
  • Loading branch information
tdauth committed Apr 1, 2019
1 parent a2fbd97 commit 1051e80
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package de.retest.guistatemachine.api

/**
* Represents transitions for one single symbol which is represented by an `de.retest.surili.commons.actions.Action` to a number of states.
* The corresponding symbol is not stored in this class but in the [[State]] from which the transitions are started.
* The corresponding symbol is not stored in this class but in the [[State]] from which the transitions are started or which the transitions lead to.
*
* @param to The states which the transitions lead to. Since it is a NFA, there can be multiple states for the same symbol.
* @param states The states which the transitions lead to or start from. Since it is a NFA, there can be multiple states for the same symbol.
* @param executionCounter The number of times all transitions for the action have been executed from the corresponding state.
* It does not matter to which state. In the legacy code this was stored as `StateGraph.executionCounter`.
*/
@SerialVersionUID(1L)
case class ActionTransitions(to: Set[State], executionCounter: Int) extends Serializable
case class ActionTransitions(states: Set[State], executionCounter: Int) extends Serializable
9 changes: 7 additions & 2 deletions src/main/scala/de/retest/guistatemachine/api/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ trait State {
* Hence, we have a set of states per action.
* In the legacy code there was a type called `AmbigueState` but a multimap simplifies the implementation.
*/
def getTransitions: Map[Action, ActionTransitions]
def getOutgoingActionTransitions: Map[Action, ActionTransitions]

/**
* Helper method to retrieve all incoming transitions.
*/
def getIncomingActionTransitions: Map[Action, ActionTransitions]

/**
* Overriding this method is required to allow the usage of a set of states.
Expand All @@ -37,7 +42,7 @@ trait State {

override def hashCode(): Int = this.getSutState.hashCode()

override def toString: String = s"sutState=$getSutState,transitions=$getTransitions"
override def toString: String = s"sutState=$getSutState,transitions=$getOutgoingActionTransitions"

/**
* Adds a new transition to the state which is only allowed by calling the methods of [[GuiStateMachine]].
Expand Down
49 changes: 35 additions & 14 deletions src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,43 @@ case class StateImpl(sutState: SutState) extends State with Serializable {
* Currently, there is no MultiMap trait for immutable maps in the Scala standard library.
* The legacy code used `AmbigueState` here which was more complicated than just a multi map.
*/
var transitions = HashMap[Action, ActionTransitions]()
private var outgoingActionTransitions = HashMap[Action, ActionTransitions]()

/**
* Redundant information but helpful to be retrieved.
*/
private var incomingActionTransitions = HashMap[Action, ActionTransitions]()

override def getSutState: SutState = this.synchronized { sutState }
override def getTransitions: Map[Action, ActionTransitions] = this.synchronized { transitions }

private[api] override def addTransition(a: Action, to: State): Int = this.synchronized {
val old = transitions.get(a)
old match {
case Some(o) =>
val updated = ActionTransitions(o.to + to, o.executionCounter + 1)
transitions = transitions + (a -> updated)
updated.executionCounter

case None =>
transitions += (a -> ActionTransitions(Set(to), 1))
1
override def getOutgoingActionTransitions: Map[Action, ActionTransitions] = this.synchronized { outgoingActionTransitions }
override def getIncomingActionTransitions: Map[Action, ActionTransitions] = this.synchronized { incomingActionTransitions }

private[api] override def addTransition(a: Action, to: State): Int = {
val executionCounter = this.synchronized {
outgoingActionTransitions.get(a) match {
case Some(oldTransitions) =>
val updatedTransitions = ActionTransitions(oldTransitions.states + to, oldTransitions.executionCounter + 1)
outgoingActionTransitions = outgoingActionTransitions + (a -> updatedTransitions)
updatedTransitions.executionCounter

case None =>
outgoingActionTransitions += (a -> ActionTransitions(Set(to), 1))
1
}
}

to.synchronized {
val other = to.asInstanceOf[StateImpl]
other.incomingActionTransitions.get(a) match {
case Some(oldTransitions) =>
val updatedTransitions = ActionTransitions(oldTransitions.states + this, oldTransitions.executionCounter + 1)
other.incomingActionTransitions = other.incomingActionTransitions + (a -> updatedTransitions)

case None =>
other.incomingActionTransitions += (a -> ActionTransitions(Set(this), 1))
}
}

executionCounter
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ class GuiStateMachinGMLSerializer(guiStateMachine: GuiStateMachine) extends GuiS

allStatesSorted.foreach { x =>
val fromVertex = x._1
val allTransitionsSorted = x._2.getTransitions.toSeq.sortWith(hashCodeComparisonOfTuples)
val allTransitionsSorted = x._2.getOutgoingActionTransitions.toSeq.sortWith(hashCodeComparisonOfTuples)

allTransitionsSorted foreach { transition =>
val actionTransitions = transition._2
val action = transition._1
actionTransitions.to.foreach { toState =>
actionTransitions.states.foreach { toState =>
val toVertex = toState.getSutState
val edge = GraphActionEdge(fromVertex, toVertex, action)
if (!graph.addEdge(fromVertex, toVertex, edge)) { throw new RuntimeException(s"Failed to add edge $edge") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
val s0SutState = createSutState(rootElementA)
val s0 = sut.getState(s0SutState)
sut.executeAction(initialSutState, action0, s0SutState)
initial.getTransitions.size shouldEqual 1
initial.getTransitions(action0).to.size shouldEqual 1
initial.getTransitions(action0).executionCounter shouldEqual 1
s0.getTransitions.size shouldEqual 0
initial.getOutgoingActionTransitions.size shouldEqual 1
initial.getOutgoingActionTransitions(action0).states.size shouldEqual 1
initial.getOutgoingActionTransitions(action0).executionCounter shouldEqual 1
initial.getIncomingActionTransitions.size shouldEqual 0
s0.getOutgoingActionTransitions.size shouldEqual 0
s0.getIncomingActionTransitions.size shouldEqual 1
s0.getIncomingActionTransitions(action0).states.size shouldEqual 1
s0.getIncomingActionTransitions(action0).executionCounter shouldEqual 1
sut.getAllExploredActions.size shouldEqual 1
sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true
sut.getActionExecutionTimes(action0) shouldEqual 1
Expand All @@ -56,10 +60,14 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
val s1SutState = createSutState(rootElementB)
val s1 = sut.getState(s1SutState)
sut.executeAction(initialSutState, action0, s1SutState)
initial.getTransitions.size shouldEqual 1
initial.getTransitions(action0).to.size shouldEqual 2
initial.getTransitions(action0).executionCounter shouldEqual 2
s1.getTransitions.size shouldEqual 0
initial.getOutgoingActionTransitions.size shouldEqual 1
initial.getOutgoingActionTransitions(action0).states.size shouldEqual 2
initial.getOutgoingActionTransitions(action0).executionCounter shouldEqual 2
initial.getIncomingActionTransitions.size shouldEqual 0
s1.getOutgoingActionTransitions.size shouldEqual 0
s1.getIncomingActionTransitions.size shouldEqual 1
s1.getIncomingActionTransitions(action0).states.size shouldEqual 1
s1.getIncomingActionTransitions(action0).executionCounter shouldEqual 1
sut.getAllExploredActions.size shouldEqual 1
sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true
sut.getActionExecutionTimes(action0) shouldEqual 2
Expand All @@ -68,10 +76,14 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
val s2SutState = createSutState(rootElementC)
val s2 = sut.getState(s2SutState)
sut.executeAction(initialSutState, action1, s2SutState)
initial.getTransitions.size shouldEqual 2
initial.getTransitions(action1).to.size shouldEqual 1
initial.getTransitions(action1).executionCounter shouldEqual 1
s2.getTransitions.size shouldEqual 0
initial.getOutgoingActionTransitions.size shouldEqual 2
initial.getOutgoingActionTransitions(action1).states.size shouldEqual 1
initial.getOutgoingActionTransitions(action1).executionCounter shouldEqual 1
initial.getIncomingActionTransitions.size shouldEqual 0
s2.getOutgoingActionTransitions.size shouldEqual 0
s2.getIncomingActionTransitions.size shouldEqual 1
s2.getIncomingActionTransitions(action1).states.size shouldEqual 1
s2.getIncomingActionTransitions(action1).executionCounter shouldEqual 1
sut.getAllExploredActions.size shouldEqual 2
sut.getActionExecutionTimes.get(action1).isDefined shouldEqual true
sut.getActionExecutionTimes(action1) shouldEqual 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,11 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft
val finalSutState = createSutState(rootElementC)

// Create the whole state machine:
val initialState = guiStateMachine.getState(initialSutState)
val finalState = guiStateMachine.getState(finalSutState)
guiStateMachine.executeAction(initialSutState, action0, finalSutState)
guiStateMachine.executeAction(initialSutState, action1, finalSutState)
guiStateMachine.executeAction(finalSutState, action0, initialSutState)
guiStateMachine.executeAction(finalSutState, action1, initialSutState)

initialState.getTransitions.size shouldEqual 2
finalState.getTransitions.size shouldEqual 2

val filePath = "./target/test_state_machine.gml"
val oldFile = new File(filePath)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ class GuiStateMachineJavaObjectStreamSerializerSpec extends AbstractApiSpec with
val loadedInitialState = guiStateMachine.getAllStates(initialSutState)
val loadedFinalState = guiStateMachine.getAllStates(finalSutState)
loadedInitialState.getSutState shouldEqual initialSutState
loadedInitialState.getTransitions.size shouldEqual 1
loadedInitialState.getTransitions.contains(action0) shouldEqual true
val loadedTransition = loadedInitialState.getTransitions(action0)
loadedInitialState.getOutgoingActionTransitions.size shouldEqual 1
loadedInitialState.getOutgoingActionTransitions.contains(action0) shouldEqual true
val loadedTransition = loadedInitialState.getOutgoingActionTransitions(action0)
loadedTransition.executionCounter shouldEqual 1
loadedTransition.to.size shouldEqual 1
loadedTransition.to.head shouldEqual loadedFinalState
loadedTransition.states.size shouldEqual 1
loadedTransition.states.head shouldEqual loadedFinalState
loadedFinalState.getSutState shouldEqual finalSutState
loadedFinalState.getTransitions.isEmpty shouldEqual true
loadedFinalState.getOutgoingActionTransitions.isEmpty shouldEqual true
}
}
}

0 comments on commit 1051e80

Please sign in to comment.