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

Commit

Permalink
Use ActionType and adapt to review and #34
Browse files Browse the repository at this point in the history
  • Loading branch information
tdauth authored and rebazer committed May 21, 2019
1 parent 2d9feab commit 10e6428
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 100 deletions.
20 changes: 10 additions & 10 deletions src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package de.retest.guistatemachine.api

import de.retest.recheck.ui.descriptors.SutState
import de.retest.surili.commons.actions.Action
import de.retest.surili.commons.actions.{Action, ActionType}

/**
* API to create a NFA which represents the current state machine of an automatic GUI test generation with the help of a genetic algorithm.
Expand All @@ -15,16 +15,15 @@ trait GuiStateMachine {

/**
* Creates a new state in the state machine based on a SUT state.
*
* @param sutStateIdentifier The abstract representation of a SUT state which should uniquely identify the state.
* @param neverExploredActionTypesCounter The initial number of unexplored action types. This number is decremented with each unexplored action which is added as a new transition.
* @param unexploredActionTypes The initially unexplored action types. This set is modified whenever an action is executed which has an unexplored action type.
* @return The newly created state.
* @throws RuntimeException If the state does already exist.
* @group possibleactions
*/
def createState(sutStateIdentifier: SutStateIdentifier, neverExploredActionTypesCounter: Int): State
def createState(sutStateIdentifier: SutStateIdentifier, unexploredActionTypes: Set[ActionType]): State

def createState(sutState: SutState, neverExploredActionTypesCounter: Int): State =
createState(new SutStateIdentifier(sutState), neverExploredActionTypesCounter)
def createState(sutState: SutState, unexploredActionTypes: Set[ActionType]): State =
createState(new SutStateIdentifier(sutState), unexploredActionTypes)

def getState(sutStateIdentifier: SutStateIdentifier): Option[State]

Expand All @@ -42,11 +41,12 @@ trait GuiStateMachine {
* @param from The state the action is executed from
* @param a The action which is executed by the user.
* @param to The state which the execution leads to.
* @param actionType The corresponding action type of a.
* @return The number of times the action has been executed.
*/
def executeAction(from: State, a: ActionIdentifier, to: State, isUnexploredActionType: Boolean): Int = from.addTransition(a, to, isUnexploredActionType)
def executeAction(from: State, a: Action, to: State, isUnexploredActionType: Boolean): Int =
executeAction(from, new ActionIdentifier(a), to, isUnexploredActionType)
def executeAction(from: State, a: ActionIdentifier, to: State, actionType: ActionType): Int = from.addTransition(a, to, actionType)
def executeAction(from: State, a: Action, to: State): Int =
executeAction(from, new ActionIdentifier(a), to, ActionType.fromAction(a))

def getAllStates: Map[SutStateIdentifier, State]

Expand Down
16 changes: 8 additions & 8 deletions src/main/scala/de/retest/guistatemachine/api/State.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.retest.guistatemachine.api

import de.retest.surili.commons.actions.ActionType

/**
* 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.
Expand All @@ -24,13 +26,11 @@ trait State {
def getIncomingActionTransitions: Map[ActionIdentifier, ActionTransitions]

/**
* @groupname possibleactions Possible Actions
* The set of possible actions has to be restricted for certain action types like ChangeValueAction. The set should always be the same for the same elements per state. It can be used for exploration strategies.
*
* @return The number of unexplored action types in this state.
* @group possibleactions
* @return The set of unexplored action types in this state.
*/
def getNeverExploredActionTypesCounter: Int
def getUnexploredActionTypes: Set[ActionType]

/**
* Overriding this method is required to allow the usage of a set of states.
Expand All @@ -48,15 +48,15 @@ trait State {

override def hashCode(): Int = this.getSutStateIdentifier.hashCode()

override def toString: String = s"State[sutStateIdentifier=$getSutStateIdentifier,neverExploredActionTypesCounter=$getNeverExploredActionTypesCounter]"
override def toString: String = s"State[sutStateIdentifier=$getSutStateIdentifier,unexploredActionTypes=$getUnexploredActionTypes]"

/**
* Adds a new transition to the state which is only allowed by calling the methods of [[GuiStateMachine]].
*
* @param a The action which represents the transition's consumed symbol.
* @param to The state which the transition leads t o.
* @param isUnexploredActionType If this flag is true, the never explored action types counter is decreased.
* @param to The state which the transition leads to.
* @param actionType The corresponding action type of a.
* @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: ActionIdentifier, to: State, isUnexploredActionType: Boolean): Int
private[api] def addTransition(a: ActionIdentifier, to: State, actionType: ActionType): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.retest.guistatemachine.api.impl

import com.typesafe.scalalogging.Logger
import de.retest.guistatemachine.api.{GuiStateMachine, State, SutStateIdentifier}
import de.retest.surili.commons.actions.ActionType

import scala.collection.mutable.HashMap

Expand All @@ -13,15 +14,17 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable {
@transient private val logger = Logger[GuiStateMachineImpl]
private var states = HashMap[SutStateIdentifier, State]()

override def createState(sutStateIdentifier: SutStateIdentifier, neverExploredActionTypesCounter: Int): State =
override def createState(sutStateIdentifier: SutStateIdentifier, unexploredActionTypes: Set[ActionType]): State =
this.synchronized {
if (states.contains(sutStateIdentifier)) {
throw new RuntimeException(s"State from SUT state $sutStateIdentifier does already exist.")
} else {
logger.info(s"Create new state from SUT state $sutStateIdentifier")
val s = StateImpl(sutStateIdentifier, neverExploredActionTypesCounter)
states += (sutStateIdentifier -> s)
s
states.get(sutStateIdentifier) match {
case Some(s) =>
logger.warn(s"State from SUT state $sutStateIdentifier does already exist.")
s
case None =>
logger.info(s"Create new state from SUT state $sutStateIdentifier")
val s = StateImpl(sutStateIdentifier, unexploredActionTypes)
states += (sutStateIdentifier -> s)
s
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package de.retest.guistatemachine.api.impl

import de.retest.guistatemachine.api.{ActionIdentifier, ActionTransitions, State, SutStateIdentifier}
import de.retest.surili.commons.actions.ActionType

import scala.collection.immutable.HashMap

@SerialVersionUID(1L)
case class StateImpl(sutState: SutStateIdentifier, var neverExploredActionTypesCounter: Int) extends State with Serializable {
case class StateImpl(sutState: SutStateIdentifier, var unexploredActionTypes: Set[ActionType]) extends State with Serializable {

/**
* Currently, there is no MultiMap trait for immutable maps in the Scala standard library.
Expand All @@ -25,13 +26,11 @@ case class StateImpl(sutState: SutStateIdentifier, var neverExploredActionTypesC
override def getIncomingActionTransitions: Map[ActionIdentifier, ActionTransitions] = this.synchronized {
incomingActionTransitions
}
override def getNeverExploredActionTypesCounter: Int = this.synchronized { neverExploredActionTypesCounter }
override def getUnexploredActionTypes: Set[ActionType] = this.synchronized { unexploredActionTypes }

private[api] override def addTransition(a: ActionIdentifier, to: State, isUnexploredActionType: Boolean): Int = {
private[api] override def addTransition(a: ActionIdentifier, to: State, actionType: ActionType): Int = {
val executionCounter = this.synchronized {
if (isUnexploredActionType) {
neverExploredActionTypesCounter = neverExploredActionTypesCounter - 1
}
unexploredActionTypes = unexploredActionTypes - actionType

outgoingActionTransitions.get(a) match {
case Some(oldTransitions) =>
Expand Down
11 changes: 11 additions & 0 deletions src/test/scala/de/retest/guistatemachine/api/AbstractApiSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@ import java.util.Arrays

import de.retest.recheck.ui.descriptors._
import de.retest.recheck.ui.image.Screenshot
import de.retest.surili.commons.actions.{ActionType, NavigateToAction}
import org.scalatest.{Matchers, WordSpec}

abstract trait AbstractApiSpec extends WordSpec with Matchers {
protected val rootElementA = getRootElement("a", 0)
protected val rootElementB = getRootElement("b", 0)
protected val rootElementC = getRootElement("c", 0)
protected val action0 = new NavigateToAction("http://google.com")
protected val action0Identifier = new ActionIdentifier(action0)
protected val actionType0 = ActionType.fromAction(action0)
protected val action1 = new NavigateToAction("http://wikipedia.org")
protected val action1Identifier = new ActionIdentifier(action1)
protected val actionType1 = ActionType.fromAction(action1)
protected val unexploredActionTypes = Set(actionType0, actionType1)

def createSutState(rootElements: RootElement*): SutState = new SutState(Arrays.asList(rootElements: _*))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
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 {
Expand All @@ -26,7 +20,7 @@ class HashIdentifierSpec extends AbstractApiSpec {
}

"be converted into a string" in {
action0Identifier.toString shouldEqual "HashIdentifier[hash=fd00ea22cb50efd96c3ff59d8900685d0d64f2cee1e77873133e7e186afd2e7f]"
action0Identifier.toString shouldEqual "ActionIdentifier[action=NavigateToAction(url=http://google.com), hash=fd00ea22cb50efd96c3ff59d8900685d0d64f2cee1e77873133e7e186afd2e7f]"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
package de.retest.guistatemachine.api.impl

import de.retest.guistatemachine.api.{AbstractApiSpec, ActionIdentifier}
import de.retest.surili.commons.actions.NavigateToAction
import de.retest.guistatemachine.api.AbstractApiSpec
import de.retest.surili.commons.actions.ActionType
import org.scalatest.BeforeAndAfterEach

class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
private val sut = new GuiStateMachineImpl
private val rootElementA = getRootElement("a", 0)
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()
Expand All @@ -28,78 +21,78 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
differentState.equals(s0) shouldBe false
differentState.hashCode() should not equal s0.hashCode()
sut.getAllStates.size shouldEqual 0
sut.createState(s0, 0)
sut.createState(s0, Set[ActionType]())
sut.getAllStates.size shouldEqual 1
the[RuntimeException] thrownBy sut.createState(s0Equal, 0)
sut.createState(s0Equal, Set[ActionType]())
sut.getAllStates.size shouldEqual 1
sut.createState(differentState, 0)
sut.createState(differentState, Set[ActionType]())
sut.getAllStates.size shouldEqual 2
}

"add two transitions to two new states for the same action and two transitions for the same action to another state" in {
val initialSutState = createSutState(rootElementA, rootElementB, rootElementC)
val initial = sut.createState(initialSutState, 2)
initial.getNeverExploredActionTypesCounter shouldEqual 2
val initial = sut.createState(initialSutState, unexploredActionTypes)
initial.getUnexploredActionTypes shouldEqual unexploredActionTypes

// execute action0 for the first time
val s0SutState = createSutState(rootElementA)
val s0 = sut.createState(s0SutState, 2)
sut.executeAction(initial, action0, s0, true) shouldEqual 1
val s0 = sut.createState(s0SutState, unexploredActionTypes)
sut.executeAction(initial, action0, s0) shouldEqual 1
initial.getOutgoingActionTransitions.size shouldEqual 1
initial.getOutgoingActionTransitions(action0Identifier).states.size shouldEqual 1
initial.getOutgoingActionTransitions(action0Identifier).executionCounter shouldEqual 1
initial.getIncomingActionTransitions.size shouldEqual 0
initial.getNeverExploredActionTypesCounter shouldEqual 1
initial.getUnexploredActionTypes shouldEqual unexploredActionTypes - actionType0
s0.getOutgoingActionTransitions.size shouldEqual 0
s0.getIncomingActionTransitions.size shouldEqual 1
s0.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1
s0.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1
s0.getNeverExploredActionTypesCounter shouldEqual 2
s0.getUnexploredActionTypes shouldEqual unexploredActionTypes

// execute action0 for the second time
val s1SutState = createSutState(rootElementB)
val s1 = sut.createState(s1SutState, 2)
sut.executeAction(initial, action0, s1, false) shouldEqual 2
val s1 = sut.createState(s1SutState, unexploredActionTypes)
sut.executeAction(initial, action0, s1) shouldEqual 2
initial.getOutgoingActionTransitions.size shouldEqual 1
initial.getOutgoingActionTransitions(action0Identifier).states.size shouldEqual 2
initial.getOutgoingActionTransitions(action0Identifier).executionCounter shouldEqual 2
initial.getIncomingActionTransitions.size shouldEqual 0
initial.getNeverExploredActionTypesCounter shouldEqual 1
initial.getUnexploredActionTypes shouldEqual unexploredActionTypes - actionType0
s1.getOutgoingActionTransitions.size shouldEqual 0
s1.getIncomingActionTransitions.size shouldEqual 1
s1.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1
s1.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1
s1.getNeverExploredActionTypesCounter shouldEqual 2
s1.getUnexploredActionTypes shouldEqual unexploredActionTypes

// execute action1 for the first time
val s2SutState = createSutState(rootElementC)
val s2 = sut.createState(s2SutState, 2)
sut.executeAction(initial, action1, s2, true) shouldEqual 1
val s2 = sut.createState(s2SutState, unexploredActionTypes)
sut.executeAction(initial, action1, s2) shouldEqual 1
initial.getOutgoingActionTransitions.size shouldEqual 2
initial.getOutgoingActionTransitions(action1Identifier).states.size shouldEqual 1
initial.getOutgoingActionTransitions(action1Identifier).executionCounter shouldEqual 1
initial.getIncomingActionTransitions.size shouldEqual 0
initial.getNeverExploredActionTypesCounter shouldEqual 0
initial.getUnexploredActionTypes shouldEqual Set()
s2.getOutgoingActionTransitions.size shouldEqual 0
s2.getIncomingActionTransitions.size shouldEqual 1
s2.getIncomingActionTransitions(action1Identifier).states.size shouldEqual 1
s2.getIncomingActionTransitions(action1Identifier).executionCounter shouldEqual 1
s2.getNeverExploredActionTypesCounter shouldEqual 2
s2.getUnexploredActionTypes shouldEqual unexploredActionTypes

// execute action1 for the second time but from s1SutState to create one incoming action from two different states
sut.executeAction(s1, action1, s2, true) shouldEqual 1
sut.executeAction(s1, action1, s2) shouldEqual 1
s1.getOutgoingActionTransitions.size shouldEqual 1
s1.getOutgoingActionTransitions(action1Identifier).states.size shouldEqual 1
s1.getOutgoingActionTransitions(action1Identifier).executionCounter shouldEqual 1
s1.getIncomingActionTransitions.size shouldEqual 1
s1.getIncomingActionTransitions(action0Identifier).states.size shouldEqual 1
s1.getIncomingActionTransitions(action0Identifier).executionCounter shouldEqual 1
s1.getNeverExploredActionTypesCounter shouldEqual 1
s1.getUnexploredActionTypes shouldEqual unexploredActionTypes - actionType1
s2.getOutgoingActionTransitions.size shouldEqual 0
s2.getIncomingActionTransitions.size shouldEqual 1
s2.getIncomingActionTransitions(action1Identifier).states shouldEqual Set(initial, s1)
s2.getIncomingActionTransitions(action1Identifier).executionCounter shouldEqual 2
s2.getNeverExploredActionTypesCounter shouldEqual 2
s2.getUnexploredActionTypes shouldEqual unexploredActionTypes
}

"store a state for the second access" in {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package de.retest.guistatemachine.api.impl

import de.retest.guistatemachine.api.{AbstractApiSpec, SutStateIdentifier}
import de.retest.surili.commons.actions.ActionType

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(sutStateAIdentifier, 1)
val s1 = StateImpl(sutStateBIdentifier, 1)
val s0 = StateImpl(sutStateAIdentifier, Set(ActionType.fromAction(action0)))
val s1 = StateImpl(sutStateBIdentifier, Set(ActionType.fromAction(action0)))
s0.equals(s1) shouldEqual false
s0.equals(10) shouldEqual false
s0.hashCode() should not equal s1.hashCode()
}

"equal" in {
val s0 = StateImpl(sutStateAIdentifier, 1)
val s1 = StateImpl(sutStateAIdentifier, 2)
val s0 = StateImpl(sutStateAIdentifier, Set(ActionType.fromAction(action0)))
val s1 = StateImpl(sutStateAIdentifier, Set(ActionType.fromAction(action0), ActionType.fromAction(action1)))
s0.equals(s1) shouldEqual true
s0.hashCode() shouldEqual s1.hashCode()
}

"be converted into a string" in {
val s0 = StateImpl(sutStateAIdentifier, 2)
s0.toString shouldEqual "State[sutStateIdentifier=SutStateIdentifier[sutState=State[descriptor=[]], hash=0e4fd44f14d365fae3a7f3579b7ef013e1167e0f4ef6de418367b81edc63450d],neverExploredActionTypesCounter=2]"
val s0 = StateImpl(sutStateAIdentifier, unexploredActionTypes)
s0.toString shouldEqual "State[sutStateIdentifier=SutStateIdentifier[sutState=State[descriptor=[]], hash=0e4fd44f14d365fae3a7f3579b7ef013e1167e0f4ef6de418367b81edc63450d],unexploredActionTypes=Set(ActionType(elementPath=, type=de.retest.surili.commons.actions.NavigateToAction))]"
}
}
}
Loading

0 comments on commit 10e6428

Please sign in to comment.