diff --git a/src/main/scala/de/retest/guistatemachine/api/ActionIdentifier.scala b/src/main/scala/de/retest/guistatemachine/api/ActionIdentifier.scala new file mode 100644 index 0000000..42b5af3 --- /dev/null +++ b/src/main/scala/de/retest/guistatemachine/api/ActionIdentifier.scala @@ -0,0 +1,5 @@ +package de.retest.guistatemachine.api + +import de.retest.surili.commons.actions.Action + +class ActionIdentifier(action: Action) extends HashIdentifier(action) diff --git a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala index a2e5222..b9c0d3b 100644 --- a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala +++ b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala @@ -13,13 +13,15 @@ import de.retest.surili.commons.actions.Action */ trait GuiStateMachine { + def getState(sutStateIdentifier: SutStateIdentifier): State + /** * Gets a state identified by the corresponding SUT state. * * @param sutState The SUT state which identifies the state. * @return The state identified by the descriptors. If there has not been any state yet, it will be added. */ - def getState(sutState: SutState): State + def getState(sutState: SutState): State = getState(new SutStateIdentifier(sutState)) /** * Executes an action from a state leading to the current state described by descriptors. @@ -29,18 +31,19 @@ trait GuiStateMachine { * @param to The state which the execution leads to. * @return The current state which the transition of a leads to. */ - def executeAction(from: State, a: Action, to: State): State + def executeAction(from: State, a: ActionIdentifier, to: State): State + def executeAction(from: State, a: Action, to: State): State = executeAction(from, new ActionIdentifier(a), to) def executeAction(fromSutState: SutState, a: Action, toSutState: SutState): State = executeAction(getState(fromSutState), a, getState(toSutState)) - def getAllStates: Map[SutState, State] + def getAllStates: Map[SutStateIdentifier, State] /** * In the legacy code this was only used to show the number of actions which have been explored by Monkey Testing. * * @return All actions which have been explored and therefore have a corresponding transition. */ - def getAllExploredActions: Set[Action] + def getAllExploredActions: Set[ActionIdentifier] /** * In the legacy code this was only used to calculate all never explored actions. @@ -48,7 +51,7 @@ trait GuiStateMachine { * * @return The number of times every explored action has been executed in the NFA. Never explored actions are not part of it. */ - def getActionExecutionTimes: Map[Action, Int] + def getActionExecutionTimes: Map[ActionIdentifier, Int] /** * Clears all states, transitions and never explored actions etc. diff --git a/src/main/scala/de/retest/guistatemachine/api/HashIdentifier.scala b/src/main/scala/de/retest/guistatemachine/api/HashIdentifier.scala new file mode 100644 index 0000000..804496b --- /dev/null +++ b/src/main/scala/de/retest/guistatemachine/api/HashIdentifier.scala @@ -0,0 +1,44 @@ +package de.retest.guistatemachine.api + +import java.io.{ByteArrayOutputStream, ObjectOutputStream, Serializable} + +/** + * Storing the whole states and actions as XML takes too much space. Therefore, we only store a hash value. The hash + * should have no collisions to uniquely identify states and actions. Since we use SHA-256 the probability is very low + * that any collisions will occur. + * + * @param serializable The serializable from which a SHA-256 is generated. + */ +@SerialVersionUID(1L) +class HashIdentifier(serializable: Serializable) extends scala.Serializable { + val hash: String = HashIdentifier.sha256Hash(HashIdentifier.serializableToString(serializable)) + + override def equals(obj: Any): Boolean = + if (!obj.isInstanceOf[HashIdentifier]) { + false + } else { + val other = obj.asInstanceOf[HashIdentifier] + hash.equals(other.hash) + } + + override def hashCode(): Int = hash.hashCode + + override def toString: String = s"hash=$hash" + +} + +object HashIdentifier { + def sha256Hash(text: String): String = + String.format("%064x", new java.math.BigInteger(1, java.security.MessageDigest.getInstance("SHA-256").digest(text.getBytes("UTF-8")))) + + def serializableToString(serializable: Serializable): String = { + val byteArrayOutputStream = new ByteArrayOutputStream() + val oos = new ObjectOutputStream(byteArrayOutputStream) + try { + oos.writeObject(serializable) + byteArrayOutputStream.toString("UTF-8") + } finally { + oos.close() + } + } +} diff --git a/src/main/scala/de/retest/guistatemachine/api/State.scala b/src/main/scala/de/retest/guistatemachine/api/State.scala index e2e0ff5..92ae269 100644 --- a/src/main/scala/de/retest/guistatemachine/api/State.scala +++ b/src/main/scala/de/retest/guistatemachine/api/State.scala @@ -1,8 +1,5 @@ package de.retest.guistatemachine.api -import de.retest.recheck.ui.descriptors.SutState -import de.retest.surili.commons.actions.Action - /** * A state should be identified by its corresponding SutState. * It consists of actions which have not been explored yet and transitions to states which build up the state machine. @@ -12,19 +9,19 @@ trait State { /** * @return The SutState which identifies this state. */ - def getSutState: SutState + def getSutStateIdentifier: SutStateIdentifier /** * NFA states can lead to different states by consuming the same symbol. * 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 getOutgoingActionTransitions: Map[Action, ActionTransitions] + def getOutgoingActionTransitions: Map[ActionIdentifier, ActionTransitions] /** * Helper method to retrieve all incoming transitions. */ - def getIncomingActionTransitions: Map[Action, ActionTransitions] + def getIncomingActionTransitions: Map[ActionIdentifier, ActionTransitions] /** * Overriding this method is required to allow the usage of a set of states. @@ -34,15 +31,15 @@ trait State { override def equals(obj: Any): Boolean = { obj match { case other: State => - this.getSutState == other.getSutState + this.getSutStateIdentifier == other.getSutStateIdentifier case _ => super.equals(obj) } } - override def hashCode(): Int = this.getSutState.hashCode() + override def hashCode(): Int = this.getSutStateIdentifier.hashCode() - override def toString: String = s"sutState=$getSutState,transitions=$getOutgoingActionTransitions" + override def toString: String = s"sutStateIdentifier=$getSutStateIdentifier" /** * Adds a new transition to the state which is only allowed by calling the methods of [[GuiStateMachine]]. @@ -51,5 +48,5 @@ trait State { * @param to The state which the transition leads t o. * @return The number of times the action has been executed from this state. The target state does not matter for this number. */ - private[api] def addTransition(a: Action, to: State): Int + private[api] def addTransition(a: ActionIdentifier, to: State): Int } diff --git a/src/main/scala/de/retest/guistatemachine/api/SutStateIdentifier.scala b/src/main/scala/de/retest/guistatemachine/api/SutStateIdentifier.scala new file mode 100644 index 0000000..6b015ab --- /dev/null +++ b/src/main/scala/de/retest/guistatemachine/api/SutStateIdentifier.scala @@ -0,0 +1,5 @@ +package de.retest.guistatemachine.api + +import de.retest.recheck.ui.descriptors.SutState + +class SutStateIdentifier(sutState: SutState) extends HashIdentifier(sutState) diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala index 5b30207..7d7a725 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala @@ -1,9 +1,7 @@ package de.retest.guistatemachine.api.impl import com.typesafe.scalalogging.Logger -import de.retest.guistatemachine.api.{GuiStateMachine, State} -import de.retest.recheck.ui.descriptors.SutState -import de.retest.surili.commons.actions.Action +import de.retest.guistatemachine.api.{ActionIdentifier, GuiStateMachine, State, SutStateIdentifier} import scala.collection.immutable.{HashMap, HashSet} @@ -13,31 +11,31 @@ import scala.collection.immutable.{HashMap, HashSet} @SerialVersionUID(1L) class GuiStateMachineImpl extends GuiStateMachine with Serializable { @transient private val logger = Logger[GuiStateMachineImpl] - private var states = new HashMap[SutState, State] + private var states = new HashMap[SutStateIdentifier, State] /** * The legacy code stored execution counters for every action. */ - private var allExploredActions = new HashSet[Action] + private var allExploredActions = new HashSet[ActionIdentifier] /** * `actionExecutionCounter` from the legacy code. * Stores the total number of executions per action. */ - private var actionExecutionTimes = new HashMap[Action, Int] + private var actionExecutionTimes = new HashMap[ActionIdentifier, Int] - override def getState(sutState: SutState): State = this.synchronized { + override def getState(sutState: SutStateIdentifier): State = this.synchronized { if (states.contains(sutState)) { states(sutState) } else { - logger.info(s"Create new state from SUT state with hash code ${sutState.hashCode()}") + logger.info(s"Create new state from SUT state $sutState") val s = StateImpl(sutState) states += (sutState -> s) s } } - override def executeAction(from: State, a: Action, to: State): State = this.synchronized { + override def executeAction(from: State, a: ActionIdentifier, to: State): State = this.synchronized { allExploredActions += a val old = actionExecutionTimes.get(a) old match { @@ -48,16 +46,16 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable { to } - override def getAllStates: Map[SutState, State] = this.synchronized { states } + override def getAllStates: Map[SutStateIdentifier, State] = this.synchronized { states } - override def getAllExploredActions: Set[Action] = this.synchronized { allExploredActions } + override def getAllExploredActions: Set[ActionIdentifier] = this.synchronized { allExploredActions } - override def getActionExecutionTimes: Map[Action, Int] = this.synchronized { actionExecutionTimes } + override def getActionExecutionTimes: Map[ActionIdentifier, Int] = this.synchronized { actionExecutionTimes } override def clear(): Unit = this.synchronized { - states = new HashMap[SutState, State] - allExploredActions = new HashSet[Action] - actionExecutionTimes = new HashMap[Action, Int] + states = new HashMap[SutStateIdentifier, State] + allExploredActions = new HashSet[ActionIdentifier] + actionExecutionTimes = new HashMap[ActionIdentifier, Int] } override def assignFrom(other: GuiStateMachine): Unit = this.synchronized { diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala b/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala index 7140f3f..91a9f14 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala @@ -1,30 +1,28 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.{ActionTransitions, State} -import de.retest.recheck.ui.descriptors.SutState -import de.retest.surili.commons.actions.Action +import de.retest.guistatemachine.api.{ActionIdentifier, ActionTransitions, State, SutStateIdentifier} import scala.collection.immutable.HashMap @SerialVersionUID(1L) -case class StateImpl(sutState: SutState) extends State with Serializable { +case class StateImpl(sutState: SutStateIdentifier) 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. */ - private var outgoingActionTransitions = HashMap[Action, ActionTransitions]() + private var outgoingActionTransitions = HashMap[ActionIdentifier, ActionTransitions]() /** * Redundant information but helpful to be retrieved. */ - private var incomingActionTransitions = HashMap[Action, ActionTransitions]() + private var incomingActionTransitions = HashMap[ActionIdentifier, ActionTransitions]() - override def getSutState: SutState = this.synchronized { sutState } - override def getOutgoingActionTransitions: Map[Action, ActionTransitions] = this.synchronized { outgoingActionTransitions } - override def getIncomingActionTransitions: Map[Action, ActionTransitions] = this.synchronized { incomingActionTransitions } + override def getSutStateIdentifier: SutStateIdentifier = this.synchronized { sutState } + override def getOutgoingActionTransitions: Map[ActionIdentifier, ActionTransitions] = this.synchronized { outgoingActionTransitions } + override def getIncomingActionTransitions: Map[ActionIdentifier, ActionTransitions] = this.synchronized { incomingActionTransitions } - private[api] override def addTransition(a: Action, to: State): Int = { + private[api] override def addTransition(a: ActionIdentifier, to: State): Int = { val executionCounter = this.synchronized { outgoingActionTransitions.get(a) match { case Some(oldTransitions) => diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphActionEdge.scala b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphActionEdge.scala index 1f0ed86..ec8debc 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphActionEdge.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphActionEdge.scala @@ -1,7 +1,6 @@ package de.retest.guistatemachine.api.impl.serialization -import de.retest.recheck.ui.descriptors.SutState -import de.retest.surili.commons.actions.Action +import de.retest.guistatemachine.api.{ActionIdentifier, SutStateIdentifier} -case class GraphActionEdge(from: SutState, to: SutState, action: Action) { +case class GraphActionEdge(from: SutStateIdentifier, to: SutStateIdentifier, action: ActionIdentifier) { override def toString: String = action.toString } diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphicsProvider.scala b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphicsProvider.scala index 2e2381c..77c1011 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphicsProvider.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GraphicsProvider.scala @@ -3,15 +3,15 @@ package de.retest.guistatemachine.api.impl.serialization import java.awt.Color import com.github.systemdir.gml.model.{EdgeGraphicDefinition, GraphicDefinition, NodeGraphicDefinition, YedGmlGraphicsProvider} -import de.retest.recheck.ui.descriptors.SutState +import de.retest.guistatemachine.api.SutStateIdentifier -class GraphicsProvider extends YedGmlGraphicsProvider[SutState, GraphActionEdge, AnyRef] { - override def getVertexGraphics(vertex: SutState): NodeGraphicDefinition = +class GraphicsProvider extends YedGmlGraphicsProvider[SutStateIdentifier, GraphActionEdge, AnyRef] { + override def getVertexGraphics(vertex: SutStateIdentifier): NodeGraphicDefinition = new NodeGraphicDefinition.Builder().setFill(Color.LIGHT_GRAY).setLineColor(Color.black).setFontStyle(GraphicDefinition.FontStyle.ITALIC).build - override def getEdgeGraphics(edge: GraphActionEdge, edgeSource: SutState, edgeTarget: SutState): EdgeGraphicDefinition = + override def getEdgeGraphics(edge: GraphActionEdge, edgeSource: SutStateIdentifier, edgeTarget: SutStateIdentifier): EdgeGraphicDefinition = new EdgeGraphicDefinition.Builder() .setTargetArrow(EdgeGraphicDefinition.ArrowType.SHORT_ARROW) .setLineType(GraphicDefinition.LineType.DASHED) .build - override def getGroupGraphics(group: AnyRef, groupElements: java.util.Set[SutState]): NodeGraphicDefinition = null + override def getGroupGraphics(group: AnyRef, groupElements: java.util.Set[SutStateIdentifier]): NodeGraphicDefinition = null } diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachinGMLSerializer.scala b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachinGMLSerializer.scala index b8b34c5..25a56dd 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachinGMLSerializer.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachinGMLSerializer.scala @@ -2,13 +2,12 @@ package de.retest.guistatemachine.api.impl.serialization import java.io.{BufferedWriter, File, FileOutputStream, OutputStreamWriter} import com.github.systemdir.gml.YedGmlWriter -import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineSerializer} -import de.retest.recheck.ui.descriptors.SutState +import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineSerializer, SutStateIdentifier} import org.jgrapht.graph.DirectedPseudograph class GuiStateMachinGMLSerializer(guiStateMachine: GuiStateMachine) extends GuiStateMachineSerializer { - type GraphType = DirectedPseudograph[SutState, GraphActionEdge] + type GraphType = DirectedPseudograph[SutStateIdentifier, GraphActionEdge] /** * Converts the state machines into GML which can be read by editors like yED. @@ -25,9 +24,9 @@ class GuiStateMachinGMLSerializer(guiStateMachine: GuiStateMachine) extends GuiS // get the gml writer val writer = - new YedGmlWriter.Builder[SutState, GraphActionEdge, AnyRef](graphicsProvider, YedGmlWriter.PRINT_LABELS: _*) + new YedGmlWriter.Builder[SutStateIdentifier, GraphActionEdge, AnyRef](graphicsProvider, YedGmlWriter.PRINT_LABELS: _*) .setEdgeLabelProvider(_.toString) - .setVertexLabelProvider(sutState => "%s - hash code: %d".format(sutState.toString, sutState.hashCode())) + .setVertexLabelProvider(_.toString) .build // write to file @@ -54,7 +53,7 @@ class GuiStateMachinGMLSerializer(guiStateMachine: GuiStateMachine) extends GuiS val actionTransitions = transition._2 val action = transition._1 actionTransitions.states.foreach { toState => - val toVertex = toState.getSutState + val toVertex = toState.getSutStateIdentifier val edge = GraphActionEdge(fromVertex, toVertex, action) if (!graph.addEdge(fromVertex, toVertex, edge)) { throw new RuntimeException(s"Failed to add edge $edge") } } diff --git a/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala b/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala index 04a9bd1..1da2772 100644 --- a/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala @@ -8,7 +8,7 @@ import org.scalatest.{Matchers, WordSpec} abstract trait AbstractApiSpec extends WordSpec with Matchers { - def createSutState(rootElements: RootElement*) : SutState = new SutState(Arrays.asList(rootElements: _*)) + def createSutState(rootElements: RootElement*): SutState = new SutState(Arrays.asList(rootElements: _*)) /** * Creates a new identifying attributes collection which should only match other identifying attributes with the same ID. diff --git a/src/test/scala/de/retest/guistatemachine/api/HashIdentifierSpec.scala b/src/test/scala/de/retest/guistatemachine/api/HashIdentifierSpec.scala new file mode 100644 index 0000000..3dd9f91 --- /dev/null +++ b/src/test/scala/de/retest/guistatemachine/api/HashIdentifierSpec.scala @@ -0,0 +1,27 @@ +package de.retest.guistatemachine.api + +import de.retest.surili.commons.actions.NavigateToAction + +class HashIdentifierSpec extends AbstractApiSpec { + private val action0 = new NavigateToAction("http://google.com") + private val action0Identifier = new HashIdentifier(action0) + private val action1 = new NavigateToAction("http://wikipedia.org") + private val action1Identifier = new HashIdentifier(action1) + + "HashIdentifier" should { + "generate SHA hashes" in { + action0Identifier.hash shouldEqual "fd00ea22cb50efd96c3ff59d8900685d0d64f2cee1e77873133e7e186afd2e7f" + action1Identifier.hash shouldEqual "240d08498736de4d893c146fd64b58b1ae1eda8c36a565919b035d86c6ee2084" + } + + "not equal" in { + action0Identifier.equals(action1Identifier) shouldEqual false + action0Identifier.hashCode() should not equal action1Identifier.hashCode() + } + + "equal" in { + action0Identifier.equals(action0Identifier) shouldEqual true + action0Identifier.hashCode() shouldEqual action0Identifier.hashCode() + } + } +} diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala index 3d1e618..2bbc886 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala @@ -1,6 +1,6 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.AbstractApiSpec +import de.retest.guistatemachine.api.{AbstractApiSpec, ActionIdentifier} import de.retest.surili.commons.actions.NavigateToAction import org.scalatest.BeforeAndAfterEach @@ -10,7 +10,9 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach { private val rootElementB = getRootElement("b", 0) private val rootElementC = getRootElement("c", 0) private val action0 = new NavigateToAction("http://google.com") + private val action0Identifier = new ActionIdentifier(action0) private val action1 = new NavigateToAction("http://wikipedia.org") + private val action1Identifier = new ActionIdentifier(action1) override def beforeEach() { sut.clear() @@ -45,64 +47,64 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach { val s0 = sut.getState(s0SutState) sut.executeAction(initialSutState, action0, s0SutState) initial.getOutgoingActionTransitions.size shouldEqual 1 - initial.getOutgoingActionTransitions(action0).states.size shouldEqual 1 - initial.getOutgoingActionTransitions(action0).executionCounter shouldEqual 1 + initial.getOutgoingActionTransitions(action0Identifier).states.size shouldEqual 1 + initial.getOutgoingActionTransitions(action0Identifier).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 + s0.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1 + s0.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1 sut.getAllExploredActions.size shouldEqual 1 - sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true - sut.getActionExecutionTimes(action0) shouldEqual 1 + sut.getActionExecutionTimes.get(action0Identifier).isDefined shouldEqual true + sut.getActionExecutionTimes(action0Identifier) shouldEqual 1 // execute action0 for the second time val s1SutState = createSutState(rootElementB) val s1 = sut.getState(s1SutState) sut.executeAction(initialSutState, action0, s1SutState) initial.getOutgoingActionTransitions.size shouldEqual 1 - initial.getOutgoingActionTransitions(action0).states.size shouldEqual 2 - initial.getOutgoingActionTransitions(action0).executionCounter shouldEqual 2 + initial.getOutgoingActionTransitions(action0Identifier).states.size shouldEqual 2 + initial.getOutgoingActionTransitions(action0Identifier).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 + s1.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1 + s1.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1 sut.getAllExploredActions.size shouldEqual 1 - sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true - sut.getActionExecutionTimes(action0) shouldEqual 2 + sut.getActionExecutionTimes.get(action0Identifier).isDefined shouldEqual true + sut.getActionExecutionTimes(action0Identifier) shouldEqual 2 // execute action1 for the first time val s2SutState = createSutState(rootElementC) val s2 = sut.getState(s2SutState) sut.executeAction(initialSutState, action1, s2SutState) initial.getOutgoingActionTransitions.size shouldEqual 2 - initial.getOutgoingActionTransitions(action1).states.size shouldEqual 1 - initial.getOutgoingActionTransitions(action1).executionCounter shouldEqual 1 + initial.getOutgoingActionTransitions(action1Identifier).states.size shouldEqual 1 + initial.getOutgoingActionTransitions(action1Identifier).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 + s2.getIncomingActionTransitions(action1Identifier).states.size shouldEqual 1 + s2.getIncomingActionTransitions(action1Identifier).executionCounter shouldEqual 1 sut.getAllExploredActions.size shouldEqual 2 - sut.getActionExecutionTimes.get(action1).isDefined shouldEqual true - sut.getActionExecutionTimes(action1) shouldEqual 1 + sut.getActionExecutionTimes.get(action1Identifier).isDefined shouldEqual true + sut.getActionExecutionTimes(action1Identifier) shouldEqual 1 // execute action1 for the second time but from s1SutState to create one incoming action from two different states sut.executeAction(s1SutState, action1, s2SutState) s1.getOutgoingActionTransitions.size shouldEqual 1 - s1.getOutgoingActionTransitions(action1).states.size shouldEqual 1 - s1.getOutgoingActionTransitions(action1).executionCounter shouldEqual 1 + s1.getOutgoingActionTransitions(action1Identifier).states.size shouldEqual 1 + s1.getOutgoingActionTransitions(action1Identifier).executionCounter shouldEqual 1 s1.getIncomingActionTransitions.size shouldEqual 1 - s1.getIncomingActionTransitions(action0).states.size shouldEqual 1 - s1.getIncomingActionTransitions(action0).executionCounter shouldEqual 1 + s1.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1 + s1.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1 s2.getOutgoingActionTransitions.size shouldEqual 0 s2.getIncomingActionTransitions.size shouldEqual 1 - s2.getIncomingActionTransitions(action1).states shouldEqual Set(initial, s1) - s2.getIncomingActionTransitions(action1).executionCounter shouldEqual 2 + s2.getIncomingActionTransitions(action1Identifier).states shouldEqual Set(initial, s1) + s2.getIncomingActionTransitions(action1Identifier).executionCounter shouldEqual 2 sut.getAllExploredActions.size shouldEqual 2 - sut.getActionExecutionTimes.get(action1).isDefined shouldEqual true - sut.getActionExecutionTimes(action1) shouldEqual 2 + sut.getActionExecutionTimes.get(action1Identifier).isDefined shouldEqual true + sut.getActionExecutionTimes(action1Identifier) shouldEqual 2 } "store a state for the second access" in { diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala index 902021c..ff99e62 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala @@ -1,32 +1,34 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.AbstractApiSpec +import de.retest.guistatemachine.api.{AbstractApiSpec, SutStateIdentifier} class StateImplSpec extends AbstractApiSpec { private val rootElementA = getRootElement("a", 0) private val rootElementB = getRootElement("b", 0) private val sutStateA = createSutState(rootElementA) + private val sutStateAIdentifier = new SutStateIdentifier(sutStateA) private val sutStateB = createSutState(rootElementB) + private val sutStateBIdentifier = new SutStateIdentifier(sutStateB) "StateImpl" should { "not equal" in { - val s0 = StateImpl(sutStateA) - val s1 = StateImpl(sutStateB) + val s0 = StateImpl(sutStateAIdentifier) + val s1 = StateImpl(sutStateBIdentifier) s0.equals(s1) shouldEqual false s0.equals(10) shouldEqual false s0.hashCode() should not equal s1.hashCode() } "equal" in { - val s0 = StateImpl(sutStateA) - val s1 = StateImpl(sutStateA) + val s0 = StateImpl(sutStateAIdentifier) + val s1 = StateImpl(sutStateAIdentifier) s0.equals(s1) shouldEqual true s0.hashCode() shouldEqual s1.hashCode() } "be converted into a string" in { - val s0 = StateImpl(sutStateA) - s0.toString shouldEqual "sutState=State[descriptor=[]],transitions=Map()" + val s0 = StateImpl(sutStateAIdentifier) + s0.toString shouldEqual "sutStateIdentifier=hash=0e4fd44f14d365fae3a7f3579b7ef013e1167e0f4ef6de418367b81edc63450d" } } } diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineGMLSerializerSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineGMLSerializerSpec.scala index ed3a39f..ce89429 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineGMLSerializerSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineGMLSerializerSpec.scala @@ -42,7 +42,8 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft f.exists() shouldEqual true f.isDirectory shouldEqual false - val lines = scala.io.Source.fromFile(filePath).mkString + val source = scala.io.Source.fromFile(filePath) + val lines = source.mkString lines shouldEqual """Creator "JGraphT GML Exporter - modified by Hayato Hess, Andreas Hofstadler" |Version 1 @@ -53,7 +54,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | node | [ | id 1 - | label "State[descriptor=[]] - hash code: 9132415" + | label "hash=acd05dfba59670825451169c470d430727226dd0dec48c64961305a0c5ab1ecb" | graphics | [ | type "rectangle" @@ -68,7 +69,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | node | [ | id 2 - | label "State[descriptor=[, , ]] - hash code: 416617022" + | label "hash=c44472d3d18e4f62b073a232e3119de9d94d3c6242b65125f454d62aced7f84e" | graphics | [ | type "rectangle" @@ -85,7 +86,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | id 3 | source 1 | target 2 - | label "NavigateToAction(url=http://google.com)" + | label "hash=240d08498736de4d893c146fd64b58b1ae1eda8c36a565919b035d86c6ee2084" | LabelGraphics | [ | model "centered" @@ -103,7 +104,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | id 4 | source 1 | target 2 - | label "NavigateToAction(url=http://wikipedia.org)" + | label "hash=fd00ea22cb50efd96c3ff59d8900685d0d64f2cee1e77873133e7e186afd2e7f" | LabelGraphics | [ | model "centered" @@ -121,7 +122,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | id 5 | source 2 | target 1 - | label "NavigateToAction(url=http://google.com)" + | label "hash=240d08498736de4d893c146fd64b58b1ae1eda8c36a565919b035d86c6ee2084" | LabelGraphics | [ | model "centered" @@ -139,7 +140,7 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | id 6 | source 2 | target 1 - | label "NavigateToAction(url=http://wikipedia.org)" + | label "hash=fd00ea22cb50efd96c3ff59d8900685d0d64f2cee1e77873133e7e186afd2e7f" | LabelGraphics | [ | model "centered" @@ -154,6 +155,8 @@ class GuiStateMachineGMLSerializerSpec extends AbstractApiSpec with BeforeAndAft | ] |] |""".stripMargin + + source.close() } "load GML " in { diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineJavaObjectStreamSerializerSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineJavaObjectStreamSerializerSpec.scala index d031c8c..8da7bd5 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineJavaObjectStreamSerializerSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/serialization/GuiStateMachineJavaObjectStreamSerializerSpec.scala @@ -3,7 +3,7 @@ package de.retest.guistatemachine.api.impl.serialization import java.io.File import de.retest.guistatemachine.api.impl.GuiStateMachineImpl -import de.retest.guistatemachine.api.{AbstractApiSpec, GuiStateMachineSerializer} +import de.retest.guistatemachine.api.{AbstractApiSpec, ActionIdentifier, GuiStateMachineSerializer, SutStateIdentifier} import de.retest.surili.commons.actions.NavigateToAction import org.scalatest.BeforeAndAfterEach @@ -25,10 +25,14 @@ class GuiStateMachineJavaObjectStreamSerializerSpec extends AbstractApiSpec with val rootElementB = getRootElement("b", 0) val rootElementC = getRootElement("c", 0) val action0 = new NavigateToAction("http://google.com") + val action0Identifier = new ActionIdentifier(action0) val action1 = new NavigateToAction("http://wikipedia.org") + val action1Identifier = new ActionIdentifier(action1) val initialSutState = createSutState(rootElementA, rootElementB, rootElementC) + val initialSutStateIdentifier = new SutStateIdentifier(initialSutState) val finalSutState = createSutState(rootElementC) + val finalSutStateIdentifier = new SutStateIdentifier(finalSutState) // Create the whole state machine: guiStateMachine.executeAction(initialSutState, action0, finalSutState) @@ -45,19 +49,19 @@ class GuiStateMachineJavaObjectStreamSerializerSpec extends AbstractApiSpec with // Verify the loaded state machine: guiStateMachine.getAllExploredActions.size shouldEqual 1 - guiStateMachine.getActionExecutionTimes(action0) shouldEqual 1 - guiStateMachine.getActionExecutionTimes.contains(action1) shouldEqual false + guiStateMachine.getActionExecutionTimes(action0Identifier) shouldEqual 1 + guiStateMachine.getActionExecutionTimes.contains(action1Identifier) shouldEqual false guiStateMachine.getAllStates.size shouldEqual 2 - val loadedInitialState = guiStateMachine.getAllStates(initialSutState) - val loadedFinalState = guiStateMachine.getAllStates(finalSutState) - loadedInitialState.getSutState shouldEqual initialSutState + val loadedInitialState = guiStateMachine.getAllStates(initialSutStateIdentifier) + val loadedFinalState = guiStateMachine.getAllStates(finalSutStateIdentifier) + loadedInitialState.getSutStateIdentifier shouldEqual initialSutStateIdentifier loadedInitialState.getOutgoingActionTransitions.size shouldEqual 1 - loadedInitialState.getOutgoingActionTransitions.contains(action0) shouldEqual true - val loadedTransition = loadedInitialState.getOutgoingActionTransitions(action0) + loadedInitialState.getOutgoingActionTransitions.contains(action0Identifier) shouldEqual true + val loadedTransition = loadedInitialState.getOutgoingActionTransitions(action0Identifier) loadedTransition.executionCounter shouldEqual 1 loadedTransition.states.size shouldEqual 1 loadedTransition.states.head shouldEqual loadedFinalState - loadedFinalState.getSutState shouldEqual finalSutState + loadedFinalState.getSutStateIdentifier shouldEqual finalSutStateIdentifier loadedFinalState.getOutgoingActionTransitions.isEmpty shouldEqual true } }