diff --git a/src/main/scala/de/retest/guistatemachine/api/example/Example.scala b/src/main/scala/de/retest/guistatemachine/api/example/Example.scala index d5c2303..578413f 100644 --- a/src/main/scala/de/retest/guistatemachine/api/example/Example.scala +++ b/src/main/scala/de/retest/guistatemachine/api/example/Example.scala @@ -15,9 +15,17 @@ object Example extends App { private val action0 = new NavigateToAction("http://google.com") val stateMachine = GuiStateMachineApi.neo4j.createStateMachine("tmp") - //stateMachine.clear() + stateMachine.clear() val startState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC)) val endState = new SutState(Arrays.asList(rootElementA)) + + stateMachine.getState(startState) + stateMachine.getState(endState) + + // TODO #19 The states do not exist after this although saved. Concurrent transactions? + + println(s"All states ${stateMachine.getAllStates.size}") + stateMachine.executeAction(startState, action0, endState) println(s"All states ${stateMachine.getAllStates.size}") diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/ActionTransitionEntity.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/ActionTransitionEntity.scala index c927ed0..0fc3fa3 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/ActionTransitionEntity.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/ActionTransitionEntity.scala @@ -1,17 +1,23 @@ package de.retest.guistatemachine.api.neo4j -import org.neo4j.ogm.annotation.{EndNode, Index, RelationshipEntity, StartNode} +import org.neo4j.ogm.annotation._ @RelationshipEntity(`type` = "ACTIONS") -class ActionTransitionEntity(s: SutStateEntity, e: SutStateEntity, a: String) extends Entity { +class ActionTransitionEntity(s: SutStateEntity, e: SutStateEntity, a: String) { + + def this() = this(null, null, null) + + @Id + @GeneratedValue + var id: java.lang.Long = 0L @Index - @StartNode val start: SutStateEntity = s + @StartNode var start: SutStateEntity = s @Index - @EndNode val end: SutStateEntity = e + @EndNode var end: SutStateEntity = e @Index - val action: String = a + var action: String = a /// The number of times this action has been executed. var counter: Int = 1 diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/Entity.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/Entity.scala deleted file mode 100644 index 9ea1588..0000000 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/Entity.scala +++ /dev/null @@ -1,8 +0,0 @@ -package de.retest.guistatemachine.api.neo4j - -import org.neo4j.ogm.annotation.{GeneratedValue, Id} - -abstract class Entity { - @Id @GeneratedValue private val id = 0L - def getId: java.lang.Long = id -} diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineNeo4J.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineNeo4J.scala index e3f7f6d..6ae53bd 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineNeo4J.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/GuiStateMachineNeo4J.scala @@ -1,26 +1,33 @@ package de.retest.guistatemachine.api.neo4j +import com.typesafe.scalalogging.Logger import de.retest.guistatemachine.api.{GuiStateMachine, State, SutStateIdentifier} import org.neo4j.ogm.cypher.{ComparisonOperator, Filter} import scala.collection.immutable.HashMap class GuiStateMachineNeo4J(var uri: String) extends GuiStateMachine { + private val logger = Logger[GuiStateMachineNeo4J] implicit val session = Neo4jSessionFactory.getSessionFactory(uri).openSession() // TODO #19 Close the session at some point? override def getState(sutStateIdentifier: SutStateIdentifier): State = { - Neo4jSessionFactory.transaction { + val result = Neo4jSessionFactory.transaction { getNodeBySutStateIdentifier(sutStateIdentifier) match { case None => // Create a new node for the SUT state in the graph database. val sutStateEntity = new SutStateEntity(sutStateIdentifier) session.save(sutStateEntity) + true // Do nothing if the node for the SUT state does already exist. - case _ => + case Some(_) => false } } + if (result) { + logger.info("Created new state with hash {}", sutStateIdentifier.hash) + } + StateNeo4J(sutStateIdentifier, this) } @@ -32,7 +39,7 @@ class GuiStateMachineNeo4J(var uri: String) extends GuiStateMachine { while (iterator.hasNext) { val node = iterator.next() - val sutState = new SutStateIdentifier(node.id) + val sutState = new SutStateIdentifier(node.hash) result = result + (sutState -> StateNeo4J(sutState, this)) } result @@ -47,7 +54,7 @@ class GuiStateMachineNeo4J(var uri: String) extends GuiStateMachine { } private[neo4j] def getNodeBySutStateIdentifier(sutStateIdentifier: SutStateIdentifier): Option[SutStateEntity] = { - val filter = new Filter("id", ComparisonOperator.EQUALS, sutStateIdentifier.hash) + val filter = new Filter("hash", ComparisonOperator.EQUALS, sutStateIdentifier.hash) val first = session.loadAll(classOf[SutStateEntity], filter).stream().findFirst() if (first.isPresent) { Some(first.get()) diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/StateNeo4J.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/StateNeo4J.scala index 729fa94..7bd103d 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/StateNeo4J.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/StateNeo4J.scala @@ -18,7 +18,7 @@ case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: G while (iterator.hasNext) { val relationship = iterator.next() val action = new ActionIdentifier(relationship.action) - val targetSutState = new SutStateIdentifier(relationship.end.id) + val targetSutState = new SutStateIdentifier(relationship.end.hash) val counter = relationship.counter val actionTransitions = if (result.contains(action)) { val existing = result(action) @@ -39,7 +39,7 @@ case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: G while (iterator.hasNext) { val relationship = iterator.next() val action = new ActionIdentifier(relationship.action) - val sourceSutState = new SutStateIdentifier(relationship.start.id) + val sourceSutState = new SutStateIdentifier(relationship.start.hash) val counter = relationship.counter val actionTransitions = if (result.contains(action)) { val existing = result(action) @@ -52,6 +52,7 @@ case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: G result } + // TODO #19 Can we somehow add the transition to the incoming and outgoing relations? private[api] override def addTransition(a: ActionIdentifier, to: State): Int = Neo4jSessionFactory.transaction { /* TODO #19 Filter for start and end states. @@ -59,7 +60,6 @@ case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: G val filterEnd = new Filter("end", ComparisonOperator.EQUALS, targetSutStateIdentifier.hash) filterStart.and(filterAction).and(filterEnd) */ - val filterAction = new Filter("action", ComparisonOperator.EQUALS, a.hash) val targetSutStateIdentifier = to.asInstanceOf[StateNeo4J].sutStateIdentifier @@ -67,18 +67,24 @@ case class StateNeo4J(sutStateIdentifier: SutStateIdentifier, guiStateMachine: G val transitions = session.loadAll(classOf[ActionTransitionEntity], filterAction).toSeq val matchingTransitions = transitions.filter(actionTransitionEntity => - actionTransitionEntity.start.id == sutStateIdentifier.hash && actionTransitionEntity.end.id == targetSutStateIdentifier.hash) + actionTransitionEntity.start.hash == sutStateIdentifier.hash && actionTransitionEntity.end.hash == targetSutStateIdentifier.hash) if (matchingTransitions.nonEmpty) { val first: ActionTransitionEntity = matchingTransitions.head first.counter = first.counter + 1 session.save(first) first.counter } else { - val sourceState = guiStateMachine.getNodeBySutStateIdentifier(sutStateIdentifier).get - val targetState = guiStateMachine.getNodeBySutStateIdentifier(targetSutStateIdentifier).get - val transition = new ActionTransitionEntity(sourceState, targetState, a.hash) - session.save(transition) - 1 + guiStateMachine.getNodeBySutStateIdentifier(sutStateIdentifier) match { + case Some(sourceState) => + guiStateMachine.getNodeBySutStateIdentifier(targetSutStateIdentifier) match { + case Some(targetState) => + val transition = new ActionTransitionEntity(sourceState, targetState, a.hash) + session.save(transition) + 1 + case None => throw new RuntimeException(s"Missing target state $targetSutStateIdentifier.") + } + case None => throw new RuntimeException(s"Missing source state $sutStateIdentifier.") + } } } } diff --git a/src/main/scala/de/retest/guistatemachine/api/neo4j/SutStateEntity.scala b/src/main/scala/de/retest/guistatemachine/api/neo4j/SutStateEntity.scala index cc5f8c2..bab37d6 100644 --- a/src/main/scala/de/retest/guistatemachine/api/neo4j/SutStateEntity.scala +++ b/src/main/scala/de/retest/guistatemachine/api/neo4j/SutStateEntity.scala @@ -1,19 +1,26 @@ package de.retest.guistatemachine.api.neo4j import de.retest.guistatemachine.api.SutStateIdentifier -import org.neo4j.ogm.annotation.{Relationship, _} - -import scala.collection.mutable +import org.neo4j.ogm.annotation._ @NodeEntity class SutStateEntity( - @Id - //@Index(unique = true) - val id: java.lang.String) { + @Index(unique = true) + @Property + var hash: java.lang.String) { def this(sutStateIdentifier: SutStateIdentifier) = this(sutStateIdentifier.hash) def this() = this("") - @Relationship(`type` = "ACTIONS", direction = Relationship.INCOMING) val incomingActionTransitions = mutable.HashSet[ActionTransitionEntity]() - @Relationship(`type` = "ACTIONS", direction = Relationship.OUTGOING) val outgoingActionTransitions = mutable.HashSet[ActionTransitionEntity]() + @Id + @GeneratedValue + var id: java.lang.Long = 0L + + @Relationship(`type` = "ACTIONS", direction = Relationship.UNDIRECTED) var actionTransitions = new java.util.LinkedList[ActionTransitionEntity]() + + // TODO #19 Which container types? + /* + @Relationship(`type` = "ACTIONS", direction = Relationship.INCOMING) var incomingActionTransitions = new java.util.LinkedList[ActionTransitionEntity]() + @Relationship(`type` = "ACTIONS", direction = Relationship.OUTGOING) var outgoingActionTransitions = new java.util.LinkedList[ActionTransitionEntity]() + */ }