diff --git a/build.sbt b/build.sbt index 20506dd..5ae92f3 100644 --- a/build.sbt +++ b/build.sbt @@ -6,6 +6,9 @@ organization := "retest" scalaVersion := "2.12.7" +// Fixes serialization issues: +fork := true + // Dependencies to represent the input of states and actions: libraryDependencies += "de.retest" % "retest-model" % "5.0.0" withSources () withJavadoc () diff --git a/src/main/scala/de/retest/guistatemachine/api/Action.scala b/src/main/scala/de/retest/guistatemachine/api/Action.scala index 8c45fc9..b2c7990 100644 --- a/src/main/scala/de/retest/guistatemachine/api/Action.scala +++ b/src/main/scala/de/retest/guistatemachine/api/Action.scala @@ -6,4 +6,5 @@ package de.retest.guistatemachine.api * 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(id: Id) +@SerialVersionUID(1L) +case class Action(id: Id) extends Serializable diff --git a/src/main/scala/de/retest/guistatemachine/api/ActionTransitions.scala b/src/main/scala/de/retest/guistatemachine/api/ActionTransitions.scala index 2dee01f..b6b60ab 100644 --- a/src/main/scala/de/retest/guistatemachine/api/ActionTransitions.scala +++ b/src/main/scala/de/retest/guistatemachine/api/ActionTransitions.scala @@ -8,4 +8,5 @@ package de.retest.guistatemachine.api * @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`. */ -case class ActionTransitions(to: Set[State], executionCounter: Int) +@SerialVersionUID(1L) +case class ActionTransitions(to: Set[State], executionCounter: Int) extends Serializable diff --git a/src/main/scala/de/retest/guistatemachine/api/Descriptors.scala b/src/main/scala/de/retest/guistatemachine/api/Descriptors.scala index 8a9d050..8c0ed83 100644 --- a/src/main/scala/de/retest/guistatemachine/api/Descriptors.scala +++ b/src/main/scala/de/retest/guistatemachine/api/Descriptors.scala @@ -5,4 +5,5 @@ import de.retest.ui.descriptors.RootElement /** * Set of root elements which identifies a state. */ -case class Descriptors(rootElements: Set[RootElement]) +@SerialVersionUID(1L) +case class Descriptors(rootElements: Set[RootElement]) extends Serializable diff --git a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala index 350a51e..472873d 100644 --- a/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala +++ b/src/main/scala/de/retest/guistatemachine/api/GuiStateMachineApi.scala @@ -3,9 +3,9 @@ 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. + * To store the state machines permanently, 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. + * [[GuiStateMachineApi.load]] allows loading state machines from a file. */ trait GuiStateMachineApi { diff --git a/src/main/scala/de/retest/guistatemachine/api/Id.scala b/src/main/scala/de/retest/guistatemachine/api/Id.scala index fcbc866..91e1ed7 100644 --- a/src/main/scala/de/retest/guistatemachine/api/Id.scala +++ b/src/main/scala/de/retest/guistatemachine/api/Id.scala @@ -1,6 +1,7 @@ package de.retest.guistatemachine.api -final case class Id(val id: Long) extends Ordered[Id] { +@SerialVersionUID(1L) +case class Id(id: Long) extends Ordered[Id] with Serializable { override def compare(that: Id): Int = this.id compare that.id } 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 ca75852..bc89748 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImpl.scala @@ -5,7 +5,7 @@ import java.io.{FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutp import de.retest.guistatemachine.api.{GuiStateMachine, GuiStateMachineApi, Id} object GuiStateMachineApiImpl extends GuiStateMachineApi { - var stateMachines = IdMap[GuiStateMachine] + val stateMachines = IdMap[GuiStateMachine]() override def createStateMachine(): Id = stateMachines.addNewElement(new GuiStateMachineImpl) @@ -18,14 +18,14 @@ object GuiStateMachineApiImpl extends GuiStateMachineApi { override def save(filePath: String): Unit = { val oos = new ObjectOutputStream(new FileOutputStream(filePath)) oos.writeObject(stateMachines) - oos.close + oos.close() } 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 + ois.close() + stateMachines.values = readStateMachines.values } } 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 fc603f1..e8b37cb 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImpl.scala @@ -6,7 +6,7 @@ import scala.collection.immutable.{HashMap, HashSet} @SerialVersionUID(1L) class GuiStateMachineImpl extends GuiStateMachine with Serializable { - // Make it accessable from the impl package for unit tests. + // Make it accessible from the impl package for unit tests. private[impl] var states = new HashMap[Descriptors, State] /** 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 1222aa9..2228726 100644 --- a/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala +++ b/src/main/scala/de/retest/guistatemachine/api/impl/IdMap.scala @@ -10,7 +10,9 @@ import scala.collection.immutable.HashMap */ @SerialVersionUID(1L) case class IdMap[T]() extends Serializable { - var values = new HashMap[Id, T] + type HashMapType = HashMap[Id, T] + + var values = new HashMapType /** * Generates a new ID based on the existing entries. @@ -36,13 +38,13 @@ case class IdMap[T]() extends Serializable { def hasElement(id: Id): Boolean = values.contains(id) def clear(): Unit = values = new HashMap[Id, T] + + override def toString = s"values: $values" } object IdMap { - def apply[T](): IdMap[T] = new IdMap[T] - - def fromValues[T](v: T*): IdMap[T] = { - val r = IdMap[T]() + def apply[T](v: T*): IdMap[T] = { + val r = new IdMap[T]() for (e <- v) r.addNewElement(e) r } 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 abd28ce..f9334d9 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineApiImplSpec.scala @@ -3,27 +3,28 @@ package de.retest.guistatemachine.api.impl import java.io.File import de.retest.guistatemachine.api.{AbstractApiSpec, Action, Descriptors, Id} +import org.scalatest.BeforeAndAfterAll -class GuiStateMachineApiImplSpec extends AbstractApiSpec { +class GuiStateMachineApiImplSpec extends AbstractApiSpec with BeforeAndAfterAll { var stateMachineId = Id(-1) + override def beforeAll = GuiStateMachineApiImpl.clear() + + override def afterAll = GuiStateMachineApiImpl.clear() + "GuiStateMachineApi" should { - "create a new state machine" in { - stateMachineId = GuiStateMachineApiImpl.createStateMachine + "create, get and remove a new state machine" in { + stateMachineId = GuiStateMachineApiImpl.createStateMachine() stateMachineId shouldEqual Id(0) - } - "get a state machine" in { val stateMachine = GuiStateMachineApiImpl.getStateMachine(stateMachineId) stateMachine.isDefined shouldBe true val fsm = stateMachine.get fsm.getActionExecutionTimes.size shouldEqual 0 fsm.getAllExploredActions.size shouldEqual 0 fsm.getAllNeverExploredActions.size shouldEqual 0 - } - "remove a state machine" in { GuiStateMachineApiImpl.removeStateMachine(stateMachineId) shouldBe true } @@ -53,8 +54,8 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec { val finalNeverExploredActions = Set(action0, action1) // Create the whole state machine: - GuiStateMachineApiImpl.clear - stateMachineId = GuiStateMachineApiImpl.createStateMachine + 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) @@ -66,7 +67,7 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec { f.isDirectory shouldEqual false // Load all state machines: - GuiStateMachineApiImpl.clear + GuiStateMachineApiImpl.clear() GuiStateMachineApiImpl.load(filePath) // Verify all loaded state machines: @@ -88,7 +89,7 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec { val loadedTransition = loadedInitialState.getTransitions(action0) loadedTransition.executionCounter shouldEqual 1 loadedTransition.to.size shouldEqual 1 - loadedTransition.to.toList(0) shouldEqual loadedFinalState + loadedTransition.to.head 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 351b0f4..f2957f9 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/GuiStateMachineImplSpec.scala @@ -14,7 +14,7 @@ class GuiStateMachineImplSpec extends AbstractApiSpec { "GuiStateMachine" should { "add two transitions to two new states for the same action and one transition to another state for another action" in { val initialDescriptors = getDescriptors - val initial = sut.getState(getDescriptors, getNeverExploredActions) + val initial = sut.getState(initialDescriptors, getNeverExploredActions) sut.getAllExploredActions.size shouldEqual 0 sut.getAllNeverExploredActions.size shouldEqual 2 sut.getActionExecutionTimes.size shouldEqual 0 @@ -64,8 +64,8 @@ class GuiStateMachineImplSpec extends AbstractApiSpec { "store a state for the second access" in { val initialDescriptors = getDescriptors - val initialFromAccess0 = sut.getState(getDescriptors, getNeverExploredActions) - val initialFromAccess1 = sut.getState(getDescriptors, getNeverExploredActions) + val initialFromAccess0 = sut.getState(initialDescriptors, getNeverExploredActions) + val initialFromAccess1 = sut.getState(initialDescriptors, getNeverExploredActions) initialFromAccess0 shouldEqual initialFromAccess1 } } 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 20ed027..ca6fa22 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/IdMapSpec.scala @@ -1,12 +1,14 @@ package de.retest.guistatemachine.api.impl +import java.io.{File, FileInputStream, FileOutputStream, ObjectInputStream, ObjectOutputStream} + import org.scalatest.{Matchers, WordSpec} class IdMapSpec extends WordSpec with Matchers { "IdMapSpec" should { "generate new IDs" in { - val map = IdMap[Int] + val map = IdMap[Int]() val id0 = map.generateId map.values = map.values + (id0 -> 1) val id1 = map.generateId 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 53393cd..2901c24 100644 --- a/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/api/impl/StateImplSpec.scala @@ -16,7 +16,7 @@ class StateImplSpec extends AbstractApiSpec { 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.equals(10) shouldEqual false s0.hashCode() should not equal s1.hashCode() } diff --git a/src/test/scala/de/retest/guistatemachine/rest/RestServiceSpec.scala b/src/test/scala/de/retest/guistatemachine/rest/RestServiceSpec.scala index fd55c10..d8fa7fe 100644 --- a/src/test/scala/de/retest/guistatemachine/rest/RestServiceSpec.scala +++ b/src/test/scala/de/retest/guistatemachine/rest/RestServiceSpec.scala @@ -1,18 +1,21 @@ package de.retest.guistatemachine.rest import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ -import akka.http.scaladsl.model.{MediaTypes, StatusCodes} +import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.testkit.ScalatestRouteTest -import de.retest.guistatemachine.rest.json.DefaultJsonFormats -import org.scalatest.{Matchers, WordSpec} import de.retest.guistatemachine.api.impl.GuiStateMachineApiImpl -import de.retest.guistatemachine.api.GuiStateMachine -import de.retest.guistatemachine.api.Id +import de.retest.guistatemachine.api.{GuiStateMachine, Id} +import de.retest.guistatemachine.rest.json.DefaultJsonFormats +import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpec} -class RestServiceSpec extends WordSpec with Matchers with ScalatestRouteTest with RestService with DefaultJsonFormats { +class RestServiceSpec extends WordSpec with Matchers with ScalatestRouteTest with RestService with DefaultJsonFormats with BeforeAndAfterAll { val sut = getRoute(GuiStateMachineApiImpl) + override def beforeAll = GuiStateMachineApiImpl.clear() + + override def afterAll = GuiStateMachineApiImpl.clear() + "The service" should { "show the default text for the GET request with the path /" in { Get("/") ~> sut ~> check {