diff --git a/build.sbt b/build.sbt index a2bdea2..20506dd 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,6 @@ scalaVersion := "2.12.7" // Dependencies to represent the input of states and actions: libraryDependencies += "de.retest" % "retest-model" % "5.0.0" withSources () withJavadoc () -libraryDependencies += "org.seleniumhq.selenium" % "selenium-java" % "2.35.0" withSources () withJavadoc () // Dependencies to provide a REST service: libraryDependencies += "com.github.scopt" % "scopt_2.12" % "3.7.0" @@ -26,7 +25,6 @@ libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % " // Test frameworks: libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" -libraryDependencies += "org.scalamock" %% "scalamock" % "4.1.0" % "test" // set the main class for 'sbt run' mainClass in (Compile, run) := Some("de.retest.guistatemachine.rest.WebServer") diff --git a/src/main/scala/de/retest/guistatemachine/api/Action.scala b/src/main/scala/de/retest/guistatemachine/api/Action.scala index 7d81d28..8c45fc9 100644 --- a/src/main/scala/de/retest/guistatemachine/api/Action.scala +++ b/src/main/scala/de/retest/guistatemachine/api/Action.scala @@ -2,10 +2,8 @@ package de.retest.guistatemachine.api /** * Interaction from the user with the GUI. - * TODO #6 Use an abstract representation of actions from retest-model instead of Selenium. The legacy code used `ActionIdentifyingAttributes`. + * TODO #6 Use an abstract representation of actions from retest-model. The legacy code used `ActionIdentifyingAttributes`. + * Selenium action types like `org.openqa.selenium.interactions.Action` should not be used since we require an `equals` + * and `hashCode` method here to use the action as a key for transitions. */ -case class Action(a: org.openqa.selenium.interactions.Action) { - - // TODO #6 Convert abstract representation of actions into string. - override def toString: String = "Selenium Action" -} +case class Action(id: Id) diff --git a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala index 2b952f5..9f81ea3 100644 --- a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala +++ b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala @@ -48,7 +48,12 @@ trait GuiStateMachine { * In the legacy code this was only used to calculate [[getAllNeverExploredActions]]. * It could be used for the visualization of the NFA to see how often actions are executed. * - * @return The number of times every explored action has been executed in the NFA. + * @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] + + /** + * Clears all states, transitions and never explored actions etc. + */ + def clear(): Unit } diff --git a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala index 4776573..350a51e 100644 --- a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala +++ b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala @@ -1,5 +1,12 @@ package de.retest.guistatemachine.api +/** + * This API allows the creation, modification and deletion of state machines ([[GuiStateMachine]]) which are created + * during test generations with the help of Genetic Algorithms. + * To store the state machines permantly, you have to call [[GuiStateMachineApi.save()]] manually. + * Otherwise, they will only be stored in the memory. + * [[GuiStateMachineApi.load()]] allows loading state machines from a file. + */ trait GuiStateMachineApi { /** @@ -25,15 +32,24 @@ trait GuiStateMachineApi { */ def getStateMachine(id: Id): Option[GuiStateMachine] + /** + * Clears all state machines. + */ + def clear(): Unit + /** * Stores all state machines on the disk. * Persistence can be useful when the state machines become quite big and the generation/modification is interrupted * and continued later. + * + * @param filePath The file which the state machines are stored into. */ - def persist(): Unit + def save(filePath: String): Unit /** - * Loads all state machines from the disk. + * Clears all current state machines and loads all state machines from the disk. + * + * @param filePath The file which the state machines are loaded from. */ - def load(): Unit + def load(filePath: String): Unit } diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala index 363c563..ca75852 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala @@ -1,11 +1,11 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineApi, Id} +import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream} -import scala.collection.immutable.HashMap +import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineApi, Id} object GuiStateMachineApiImpl extends GuiStateMachineApi { - val stateMachines = IdMap(new HashMap[Id, GuiStateMachine]) + var stateMachines = IdMap[GuiStateMachine] override def createStateMachine(): Id = stateMachines.addNewElement(new GuiStateMachineImpl) @@ -13,11 +13,19 @@ object GuiStateMachineApiImpl extends GuiStateMachineApi { override def getStateMachine(id: Id): Option[GuiStateMachine] = stateMachines.getElement(id) - override def persist(): Unit = { - // TODO #9 store on the disk + override def clear(): Unit = stateMachines.clear() + + override def save(filePath: String): Unit = { + val oos = new ObjectOutputStream(new FileOutputStream(filePath)) + oos.writeObject(stateMachines) + oos.close } - override def load(): Unit = { - // TODO #9 Load from the disk + override def load(filePath: String): Unit = { + clear() + val ois = new ObjectInputStream(new FileInputStream(filePath)) + val readStateMachines = ois.readObject.asInstanceOf[IdMap[GuiStateMachine]] + ois.close + stateMachines = readStateMachines } } 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 68920a1..fc603f1 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,13 @@ package de.retest.guistatemachine.api.impl import de.retest.guistatemachine.api.{Action, Descriptors, GuiStateMachine, State} + import scala.collection.immutable.{HashMap, HashSet} -class GuiStateMachineImpl extends GuiStateMachine { - var states = new HashMap[Descriptors, State] + +@SerialVersionUID(1L) +class GuiStateMachineImpl extends GuiStateMachine with Serializable { + // Make it accessable from the impl package for unit tests. + private[impl] var states = new HashMap[Descriptors, State] /** * In the legacy code we had `getAllNeverExploredActions` which had to collect them from all states and make sure they were never executed. @@ -51,4 +55,11 @@ class GuiStateMachineImpl extends GuiStateMachine { override def getAllExploredActions: Set[Action] = allExploredActions override def getActionExecutionTimes: Map[Action, Int] = actionExecutionTimes + + override def clear(): Unit = { + states = HashMap[Descriptors, State]() + allNeverExploredActions = HashSet[Action]() + allExploredActions = HashSet[Action]() + actionExecutionTimes = HashMap[Action, Int]() + } } diff --git a/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala b/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala index 5e62db7..1222aa9 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala @@ -8,7 +8,9 @@ import scala.collection.immutable.HashMap * This custom type allows storing values using [[Id]] as key. * We cannot extend immutable maps in Scala, so we have to keep it as field. */ -case class IdMap[T](var values: scala.collection.immutable.Map[Id, T] = new HashMap[Id, T]) { +@SerialVersionUID(1L) +case class IdMap[T]() extends Serializable { + var values = new HashMap[Id, T] /** * Generates a new ID based on the existing entries. @@ -32,9 +34,13 @@ case class IdMap[T](var values: scala.collection.immutable.Map[Id, T] = new Hash def getElement(id: Id): Option[T] = values.get(id) def hasElement(id: Id): Boolean = values.contains(id) + + def clear(): Unit = values = new HashMap[Id, T] } object IdMap { + def apply[T](): IdMap[T] = new IdMap[T] + def fromValues[T](v: T*): IdMap[T] = { val r = IdMap[T]() for (e <- v) r.addNewElement(e) 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 db2fc00..b2db85b 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala @@ -4,7 +4,8 @@ import de.retest.guistatemachine.api.{Action, ActionTransitions, Descriptors, St import scala.collection.immutable.HashMap -class StateImpl(descriptors: Descriptors, var neverExploredActions: Set[Action]) extends State { +@SerialVersionUID(1L) +class StateImpl(descriptors: Descriptors, var neverExploredActions: Set[Action]) extends State with Serializable { /** * TODO #4 Currently, there is no MultiMap trait for immutable maps in the Scala standard library. diff --git a/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala b/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala index 33fa97d..26814a2 100644 --- a/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala @@ -4,10 +4,9 @@ import java.util.{Arrays, Collections} import de.retest.ui.descriptors._ import de.retest.ui.image.Screenshot -import org.scalamock.scalatest.MockFactory import org.scalatest.{Matchers, WordSpec} -abstract trait AbstractApiSpec extends WordSpec with Matchers with MockFactory { +abstract trait AbstractApiSpec extends WordSpec with Matchers { /** * Creates a new identifying attributes collection which should only match other identifying attributes with the same ID. @@ -34,6 +33,4 @@ abstract trait AbstractApiSpec extends WordSpec with Matchers with MockFactory { 0, "My Window" ) - - def getAction(): org.openqa.selenium.interactions.Action = mock[org.openqa.selenium.interactions.Action] } diff --git a/src/test/scala/de/retest/guistatemachine/api/ActionSpec.scala b/src/test/scala/de/retest/guistatemachine/api/ActionSpec.scala new file mode 100644 index 0000000..b31557a --- /dev/null +++ b/src/test/scala/de/retest/guistatemachine/api/ActionSpec.scala @@ -0,0 +1,20 @@ +package de.retest.guistatemachine.api + +import org.scalatest.{Matchers, WordSpec} + +class ActionSpec extends WordSpec with Matchers { + + "Action" should { + "equal and not equal" in { + val action0 = Action(Id(0)) + val action1 = Action(Id(1)) + action0.equals(action0) shouldEqual true + action0.equals(action1) shouldEqual false + } + + "toString" in { + val action0 = Action(Id(0)) + action0.toString shouldEqual "Action(Id(0))" + } + } +} diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala index 6fe082c..abd28ce 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala @@ -1,6 +1,8 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.{AbstractApiSpec, Id} +import java.io.File + +import de.retest.guistatemachine.api.{AbstractApiSpec, Action, Descriptors, Id} class GuiStateMachineApiImplSpec extends AbstractApiSpec { @@ -24,5 +26,72 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec { "remove a state machine" in { GuiStateMachineApiImpl.removeStateMachine(stateMachineId) shouldBe true } + + "clear all state machines" in { + GuiStateMachineApiImpl.createStateMachine shouldEqual Id(0) + GuiStateMachineApiImpl.createStateMachine shouldEqual Id(1) + GuiStateMachineApiImpl.createStateMachine shouldEqual Id(2) + GuiStateMachineApiImpl.clear() + GuiStateMachineApiImpl.getStateMachine(Id(2)).isEmpty shouldEqual true + } + + "save and load" in { + val filePath = "./test_state_machines" + val oldFile = new File(filePath) + + if (oldFile.exists()) oldFile.delete() shouldEqual true + + val rootElementA = getRootElement("a") + val rootElementB = getRootElement("b") + val rootElementC = getRootElement("c") + val action0 = Action(Id(0)) + val action1 = Action(Id(1)) + + val initialDescriptors = Descriptors(Set(rootElementA, rootElementB, rootElementC)) + val initialNeverExploredActions = Set(action0, action1) + val finalDescriptors = Descriptors(Set(rootElementC)) + val finalNeverExploredActions = Set(action0, action1) + + // Create the whole state machine: + GuiStateMachineApiImpl.clear + stateMachineId = GuiStateMachineApiImpl.createStateMachine + val stateMachine = GuiStateMachineApiImpl.getStateMachine(stateMachineId).get + val initialState = stateMachine.getState(initialDescriptors, initialNeverExploredActions) + val finalState = stateMachine.executeAction(initialState, action0, finalDescriptors, finalNeverExploredActions) + + // Save all state machines: + GuiStateMachineApiImpl.save(filePath) + val f = new File(filePath) + f.exists() shouldEqual true + f.isDirectory shouldEqual false + + // Load all state machines: + GuiStateMachineApiImpl.clear + GuiStateMachineApiImpl.load(filePath) + + // Verify all loaded state machines: + val loadedStateMachineOp = GuiStateMachineApiImpl.getStateMachine(stateMachineId) + loadedStateMachineOp.isDefined shouldEqual true + val loadedStateMachine = loadedStateMachineOp.get.asInstanceOf[GuiStateMachineImpl] + + loadedStateMachine.getAllExploredActions.size shouldEqual 1 + loadedStateMachine.getAllNeverExploredActions.size shouldEqual 1 + loadedStateMachine.getActionExecutionTimes(action0) shouldEqual 1 + loadedStateMachine.getActionExecutionTimes.contains(action1) shouldEqual false + loadedStateMachine.states.size shouldEqual 2 + val loadedInitialState = loadedStateMachine.states(initialDescriptors) + val loadedFinalState = loadedStateMachine.states(finalDescriptors) + loadedInitialState.getDescriptors shouldEqual initialDescriptors + loadedInitialState.getTransitions.size shouldEqual 1 + loadedInitialState.getTransitions.contains(action0) shouldEqual true + loadedInitialState.getNeverExploredActions.size shouldEqual 1 + val loadedTransition = loadedInitialState.getTransitions(action0) + loadedTransition.executionCounter shouldEqual 1 + loadedTransition.to.size shouldEqual 1 + loadedTransition.to.toList(0) shouldEqual loadedFinalState + loadedFinalState.getDescriptors shouldEqual finalDescriptors + loadedFinalState.getTransitions.isEmpty shouldEqual true + loadedFinalState.getNeverExploredActions shouldEqual finalNeverExploredActions + } } } 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 7a5c467..351b0f4 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, Action, Descriptors} +import de.retest.guistatemachine.api.{AbstractApiSpec, Action, Descriptors, Id} class GuiStateMachineImplSpec extends AbstractApiSpec { @@ -8,8 +8,8 @@ class GuiStateMachineImplSpec extends AbstractApiSpec { val rootElementA = getRootElement("a") val rootElementB = getRootElement("b") val rootElementC = getRootElement("c") - val action0Mock = getAction() - val action1Mock = getAction() + val action0 = Action(Id(0)) + val action1 = Action(Id(1)) "GuiStateMachine" should { "add two transitions to two new states for the same action and one transition to another state for another action" in { @@ -21,45 +21,45 @@ class GuiStateMachineImplSpec extends AbstractApiSpec { // execute action0Mock for the first time val s0Descriptors = Descriptors(Set(rootElementA)) - val s0 = sut.executeAction(initial, Action(action0Mock), s0Descriptors, getNeverExploredActions) + val s0 = sut.executeAction(initial, action0, s0Descriptors, getNeverExploredActions) initial.getNeverExploredActions.size shouldEqual 1 initial.getTransitions.size shouldEqual 1 - initial.getTransitions(Action(action0Mock)).to.size shouldEqual 1 - initial.getTransitions(Action(action0Mock)).executionCounter shouldEqual 1 + initial.getTransitions(action0).to.size shouldEqual 1 + initial.getTransitions(action0).executionCounter shouldEqual 1 s0.getNeverExploredActions.size shouldEqual 2 s0.getTransitions.size shouldEqual 0 sut.getAllExploredActions.size shouldEqual 1 sut.getAllNeverExploredActions.size shouldEqual 1 - sut.getActionExecutionTimes.get(Action(action0Mock)).isDefined shouldEqual true - sut.getActionExecutionTimes.get(Action(action0Mock)).get shouldEqual 1 + sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true + sut.getActionExecutionTimes.get(action0).get shouldEqual 1 // execute action0Mock for the second time val s1Descriptors = Descriptors(Set(rootElementB)) - val s1 = sut.executeAction(initial, Action(action0Mock), s1Descriptors, getNeverExploredActions) + val s1 = sut.executeAction(initial, action0, s1Descriptors, getNeverExploredActions) initial.getNeverExploredActions.size shouldEqual 1 initial.getTransitions.size shouldEqual 1 - initial.getTransitions(Action(action0Mock)).to.size shouldEqual 2 - initial.getTransitions(Action(action0Mock)).executionCounter shouldEqual 2 + initial.getTransitions(action0).to.size shouldEqual 2 + initial.getTransitions(action0).executionCounter shouldEqual 2 s1.getNeverExploredActions.size shouldEqual 2 s1.getTransitions.size shouldEqual 0 sut.getAllExploredActions.size shouldEqual 1 sut.getAllNeverExploredActions.size shouldEqual 1 - sut.getActionExecutionTimes.get(Action(action0Mock)).isDefined shouldEqual true - sut.getActionExecutionTimes.get(Action(action0Mock)).get shouldEqual 2 + sut.getActionExecutionTimes.get(action0).isDefined shouldEqual true + sut.getActionExecutionTimes.get(action0).get shouldEqual 2 // execute action1Mock for the first time val s2Descriptors = Descriptors(Set(rootElementC)) - val s2 = sut.executeAction(initial, Action(action1Mock), s2Descriptors, getNeverExploredActions) + val s2 = sut.executeAction(initial, action1, s2Descriptors, getNeverExploredActions) initial.getNeverExploredActions.size shouldEqual 0 initial.getTransitions.size shouldEqual 2 - initial.getTransitions(Action(action1Mock)).to.size shouldEqual 1 - initial.getTransitions(Action(action1Mock)).executionCounter shouldEqual 1 + initial.getTransitions(action1).to.size shouldEqual 1 + initial.getTransitions(action1).executionCounter shouldEqual 1 s2.getNeverExploredActions.size shouldEqual 2 s2.getTransitions.size shouldEqual 0 sut.getAllExploredActions.size shouldEqual 2 sut.getAllNeverExploredActions.size shouldEqual 0 - sut.getActionExecutionTimes.get(Action(action1Mock)).isDefined shouldEqual true - sut.getActionExecutionTimes.get(Action(action1Mock)).get shouldEqual 1 + sut.getActionExecutionTimes.get(action1).isDefined shouldEqual true + sut.getActionExecutionTimes.get(action1).get shouldEqual 1 } "store a state for the second access" in { @@ -71,5 +71,5 @@ class GuiStateMachineImplSpec extends AbstractApiSpec { } def getDescriptors: Descriptors = Descriptors(Set(rootElementA, rootElementB, rootElementC)) - def getNeverExploredActions: Set[Action] = Set(Action(action0Mock), Action(action1Mock)) + def getNeverExploredActions: Set[Action] = Set(action0, action1) } diff --git a/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala b/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala index 8901701..20ed027 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala @@ -1,18 +1,14 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.Id import org.scalatest.{Matchers, WordSpec} -import scala.collection.immutable.HashMap - class IdMapSpec extends WordSpec with Matchers { "IdMapSpec" should { "generate new IDs" in { - val hashMap = new HashMap[Id, Int] - val map = IdMap(hashMap) + val map = IdMap[Int] val id0 = map.generateId - map.values = hashMap + (id0 -> 1) + map.values = map.values + (id0 -> 1) val id1 = map.generateId id0.id shouldEqual 0 id1.id shouldEqual 1 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 71efc21..53393cd 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala @@ -1,37 +1,39 @@ package de.retest.guistatemachine.api.impl -import de.retest.guistatemachine.api.{AbstractApiSpec, Action, Descriptors} +import de.retest.guistatemachine.api.{AbstractApiSpec, Action, Descriptors, Id} class StateImplSpec extends AbstractApiSpec { - val descriptorsA = Descriptors(Set(getRootElement("a"))) - val descriptorsB = Descriptors(Set(getRootElement("b"))) - val action0Mock = getAction() - val action1Mock = getAction() + val rootElementA = getRootElement("a") + val rootElementB = getRootElement("b") + val descriptorsA = Descriptors(Set(rootElementA)) + val descriptorsB = Descriptors(Set(rootElementB)) + val action0 = Action(Id(0)) + val action1 = Action(Id(1)) "StateImpl" should { "not equal" in { - val s0 = new StateImpl(descriptorsA, Set(Action(action0Mock))) - val s1 = new StateImpl(descriptorsB, Set(Action(action1Mock))) + val s0 = new StateImpl(descriptorsA, Set(action0)) + val s1 = new StateImpl(descriptorsB, Set(action1)) s0.equals(s1) shouldEqual false s0.equals(null) shouldEqual false s0.hashCode() should not equal s1.hashCode() } "equal" in { - val s0 = new StateImpl(descriptorsA, Set(Action(action0Mock))) - val s1 = new StateImpl(descriptorsA, Set(Action(action1Mock))) + val s0 = new StateImpl(descriptorsA, Set(action0)) + val s1 = new StateImpl(descriptorsA, Set(action1)) s0.equals(s1) shouldEqual true s0.hashCode() shouldEqual s1.hashCode() } "be converted into a string" in { - val s0 = new StateImpl(descriptorsA, Set(Action(action0Mock))) - s0.toString shouldEqual "descriptors=Descriptors(Set()),neverExploredActions=Set(Selenium Action),transitions=Map()" + val s0 = new StateImpl(descriptorsA, Set(action0)) + s0.toString shouldEqual "descriptors=Descriptors(Set()),neverExploredActions=Set(Action(Id(0))),transitions=Map()" } "have a random action" in { - val s0 = new StateImpl(descriptorsA, Set(Action(action0Mock))) + val s0 = new StateImpl(descriptorsA, Set(action0)) s0.getRandomAction().isDefined shouldEqual true }