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

Commit

Permalink
Store SutStates as string properties
Browse files Browse the repository at this point in the history
Use the utility class XmlTransformerUtil for the XML conversion.
Besides, improve the transaction code.
  • Loading branch information
tdauth committed Mar 26, 2019
1 parent aa5288a commit 80db550
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 29 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,8 @@ There can be different backends which manage the state machine.
### Neo4J

This backend uses the GraphDB [Neo4J](https://neo4j.com/) (community edition) with an embedded database.
Each state machine is represented by a separate graph database stored in a separate file.

The nodes all have the property "sutState" which contains the corresponding SUT state and can be used as index to query the nodes.
Each state machine is represented by a separate graph database stored in a separate directory.
The nodes all have the property "sutState" which contains the corresponding SUT state serialized as XML.
The relationship types correspond to actions.
Each relation has the property "counter" which contains the execution counter of the action.

We need to use an [Object Graph Mapper](https://neo4j.com/docs/ogm-manual/current/introduction/) to store the corresponding SUT states in the nodes.

See also <https://neo4j.com/docs/java-reference/current/tutorials-java-embedded/>
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ libraryDependencies += "de.retest" % "surili-commons" % "0.1.0-SNAPSHOT" withSou

// Dependencies for a graph database:
libraryDependencies += "org.neo4j" % "neo4j" % "3.0.1"
libraryDependencies += "org.neo4j" % "neo4j-ogm-core" % "3.1.7"
libraryDependencies += "org.neo4j" % "neo4j-ogm-embedded-driver" % "3.1.7"

// Dependencies to write GML files for yEd:
libraryDependencies += "com.github.systemdir.gml" % "GMLWriterForYed" % "2.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ object Example extends App {
private val action1 = new NavigateToAction("http://wikipedia.org")

val stateMachine = GuiStateMachineApi.neo4j.createStateMachine("tmp")
//stateMachine.clear()
val startState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC))
val endState = new SutState(Arrays.asList(rootElementA))
stateMachine.executeAction(startState, action0, endState)

println(s"All states ${stateMachine.getAllStates.size}")

/**
* Creates a new identifying attributes collection which should only match other identifying attributes with the same ID.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package de.retest.guistatemachine.api.neo4j
import de.retest.guistatemachine.api.{GuiStateMachine, State}
import de.retest.recheck.ui.descriptors.SutState
import de.retest.surili.commons.actions.Action
import org.neo4j.graphdb.{GraphDatabaseService, Node, Transaction}
import org.neo4j.graphdb.{GraphDatabaseService, Node, ResourceIterator, Transaction}

import scala.collection.immutable.HashMap

Expand All @@ -15,9 +15,12 @@ class GuiStateMachineNeo4J(var graphDb: GraphDatabaseService) extends GuiStateMa
tx = Some(graphDb.beginTx)
getNodeBySutState(sutState) match {
case None => {
// Create a new node for the sutState in the graph database.
val node = graphDb.createNode
node.addLabel(SutStateLabel)
// TODO #19 SutState is not a supported property value!
node.setProperty("sutState", sutState)
val value = new SutStateConverter().toGraphProperty(sutState)
node.setProperty("sutState", value)
}
case _ =>
}
Expand All @@ -36,39 +39,37 @@ class GuiStateMachineNeo4J(var graphDb: GraphDatabaseService) extends GuiStateMa

override def getAllStates: Map[SutState, State] = {
var tx: Option[Transaction] = None
val allNodes = try {
try {
tx = Some(graphDb.beginTx)
val allNodes = graphDb.getAllNodes()
var result = HashMap[SutState, State]()
val iterator = allNodes.iterator()

while (iterator.hasNext) {
val node = iterator.next()
val sutState = getSutState(node)
result = result + (sutState -> new StateNeo4J(sutState, this))
}
tx.get.success()
allNodes
result
} finally {
if (tx.isDefined) { tx.get.close() }
}

var result = HashMap[SutState, State]()
val iterator = allNodes.iterator()

while (iterator.hasNext) {
val node = iterator.next()
val sutState = node.getProperty("sutState").asInstanceOf[SutState]
result = result + (sutState -> new StateNeo4J(sutState, this))
}
result
}

override def getAllExploredActions: Set[Action] = Set() // TODO #19 get all relationships in a transaction

override def getActionExecutionTimes: Map[Action, Int] = Map() // TODO #19 get all execution time properties "counter" from all actions

override def clear(): Unit = {
var tx: Transaction = null
var tx: Option[Transaction] = None
try {
tx = graphDb.beginTx
tx = Some(graphDb.beginTx)
// Deletes all nodes and relationships.
graphDb.execute("MATCH (n)\nDETACH DELETE n")
tx.success
tx.get.success
} finally {
if (tx != null) { tx.close() }
if (tx.isDefined) { tx.get.close() }
}
}

Expand All @@ -78,10 +79,23 @@ class GuiStateMachineNeo4J(var graphDb: GraphDatabaseService) extends GuiStateMa
graphDb = otherStateMachine.graphDb
}

private[neo4j] def getSutState(node: Node): SutState = {
val value = node.getProperty("sutState").asInstanceOf[String]
new SutStateConverter().toEntityAttribute(value)
}

// TODO #19 Create an index on the property "sutState": https://neo4j.com/docs/cypher-manual/current/schema/index/#schema-index-create-a-single-property-index
private[neo4j] def getNodeBySutState(sutState: SutState): Option[Node] = {
val nodes = graphDb.findNodes(SutStateLabel, "sutState", sutState)
val first = nodes.stream().findFirst()
var nodes: Option[ResourceIterator[Node]] = None
val first = try {
val value = new SutStateConverter().toGraphProperty(sutState)
nodes = Some(graphDb.findNodes(SutStateLabel, "sutState", value))
nodes.get.stream().findFirst()
} finally {
if (nodes.isDefined) {
nodes.get.close()
}
}
if (first.isPresent) {
Some(first.get())
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ case class StateNeo4J(sutState: SutState, guiStateMachine: GuiStateMachineNeo4J)
val relationship = iterator.next()
val relationshipTypeAction = relationship.getType.asInstanceOf[RelationshipTypeAction]
val action = relationshipTypeAction.action
val sutState = relationship.getEndNode.getProperty("sutState").asInstanceOf[SutState]
val sutState = guiStateMachine.getSutState(relationship.getEndNode)
val actionTransitions = if (result.contains(action)) {
val existing = result.get(action).get
ActionTransitions(existing.to ++ Set(new StateNeo4J(sutState, guiStateMachine)), existing.executionCounter + 1)
Expand All @@ -41,15 +41,16 @@ case class StateNeo4J(sutState: SutState, guiStateMachine: GuiStateMachineNeo4J)
val iterator = existingRelationships.iterator()
while (iterator.hasNext && existingRelationship.isEmpty) {
val relationship = iterator.next()
val sutState = relationship.getEndNode().getProperty("sutState").asInstanceOf[SutState]
val sutState = guiStateMachine.getSutState(relationship.getEndNode)
if (to.getSutState == sutState) {
existingRelationship = Some(relationship)
}
}

val counter = if (existingRelationship.isEmpty) {
val other = guiStateMachine.getNodeBySutState(to.getSutState).get // TODO #19 What happens if the node is not found?
node.createRelationshipTo(other, relationshipTypeAction)
val relationship = node.createRelationshipTo(other, relationshipTypeAction)
relationship.setProperty("counter", 1)
1
} else {
val r = existingRelationship.get
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.retest.guistatemachine.api.neo4j
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets

import de.retest.recheck.XmlTransformerUtil
import de.retest.recheck.ui.descriptors.SutState
import org.neo4j.ogm.typeconversion.AttributeConverter

/*
* https://github.com/neo4j/neo4j-ogm/blob/master/neo4j-ogm-docs/src/main/asciidoc/reference/conversion.adoc
*/
class SutStateConverter extends AttributeConverter[SutState, String] {
def toGraphProperty(value: SutState): String = XmlTransformerUtil.getXmlTransformer.toXML(value)
def toEntityAttribute(value: String): SutState =
XmlTransformerUtil.getXmlTransformer.fromXML[SutState](new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)))
}

0 comments on commit 80db550

Please sign in to comment.