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

Commit

Permalink
Do not store never explored actions #14
Browse files Browse the repository at this point in the history
Random actions will be provided by a different API.
  • Loading branch information
tdauth committed Jan 22, 2019
1 parent c2e3bf3 commit 7e308cc
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 120 deletions.
24 changes: 4 additions & 20 deletions src/main/scala/de/retest/guistatemachine/api/GuiStateMachine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ import de.retest.ui.descriptors.SutState
trait GuiStateMachine {

/**
* Gets a state identified by descriptors and with its initial never explored actions.
* Gets a state identified by the corresponding SUT state.
*
* @param sutState The SUT state which identifies the state.
* @param neverExploredActions All actions which have never been explored from the state.
* @return The state identified by the descriptors. If there has not been any state yet, it will be added.
*/
def getState(sutState: SutState, neverExploredActions: Set[Action]): State
def getState(sutState: SutState): State = getState(sutState, Set())
def getState(sutState: SutState): State

/**
* Executes an action from a state leading to the current state described by descriptors.
Expand All @@ -32,25 +30,11 @@ trait GuiStateMachine {
* @return The current state which the transition of a leads to.
*/
def executeAction(from: State, a: Action, to: State): State
def executeAction(fromSutState: SutState,
fromNeverExploredActions: Set[Action],
a: Action,
toSutState: SutState,
toNeverExploredActions: Set[Action]): State =
executeAction(getState(fromSutState, fromNeverExploredActions), a, getState(toSutState, toNeverExploredActions))

def executeAction(fromSutState: SutState, a: Action, toSutState: SutState): State =
executeAction(fromSutState, Set(), a, toSutState, Set())
executeAction(fromSutState, a, toSutState)

def getAllStates: Map[SutState, State]

/**
* Can be used by the GA to generate new test cases.
*
* @return All actions which have not been explored yet.
*/
def getAllNeverExploredActions: Set[Action]

/**
* In the legacy code this was only used to show the number of actions which have been explored by Monkey Testing.
*
Expand All @@ -59,7 +43,7 @@ trait GuiStateMachine {
def getAllExploredActions: Set[Action]

/**
* In the legacy code this was only used to calculate [[getAllNeverExploredActions]].
* In the legacy code this was only used to calculate all never explored actions.
* 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. Never explored actions are not part of it.
Expand Down
27 changes: 1 addition & 26 deletions src/main/scala/de/retest/guistatemachine/api/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,13 @@ trait State {
*/
def getSutState: SutState

/**
* @return All actions which have not already been explored/executed from this state.
*/
def getNeverExploredActions: Set[Action]

/**
* NFA states can lead to different states by consuming the same symbol.
* Hence, we have a set of states per action.
* In the legacy code there was a type called `AmbigueState` but a multimap simplifies the implementation.
*/
def getTransitions: Map[Action, ActionTransitions]

/**
* This was used in the legacy code for Monkey testing.
*
* @return Returns a random action or an empty value if there are none left.
*/
def getRandomAction(): Option[Action] = {
val r = getAllActions().toVector
if (r.isEmpty) { None } else {
val rnd = new Random()
Some(r(rnd.nextInt(r.size)))
}
}

/**
* Overriding this method is required to allow the usage of a set of states.
* Comparing the descriptors should check for the equality of all root elements which compares the identifying attributes and the contained components
Expand All @@ -57,7 +39,7 @@ trait State {

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

override def toString: String = s"sutState=$getSutState,neverExploredActions=$getNeverExploredActions,transitions=$getTransitions"
override def toString: String = s"sutState=$getSutState,transitions=$getTransitions"

/**
* Adds a new transition to the state which is only allowed by calling the methods of [[GuiStateMachine]].
Expand All @@ -67,11 +49,4 @@ trait State {
* @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: Action, to: State): Int

/**
* This was named `getRandomActions` in the legacy code but actually returned all actions.
*
* @return All actions (explored + unexplored).
*/
private def getAllActions(): Set[Action] = getNeverExploredActions ++ getTransitions.keySet
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable {
// Make it accessible from the impl package for unit tests.
private var states = new HashMap[SutState, State]

/**
* In the legacy code we had `getAllNeverExploredActions` which had to collect them from all states and make sure they were never executed.
* Storing them directly in a set improves efficiency.
*/
var allNeverExploredActions = new HashSet[Action]

/**
* The legacy code stored execution counters for every action.
*/
Expand All @@ -34,21 +28,19 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable {
*/
var actionExecutionTimes = new HashMap[Action, Int]

override def getState(sutState: SutState, neverExploredActions: Set[Action]): State = {
override def getState(sutState: SutState): State = {
if (states.contains(sutState)) {
states(sutState)
} else {
allNeverExploredActions = allNeverExploredActions ++ (neverExploredActions -- allExploredActions)
logger.info(s"Create new state from SUT state with hash code ${sutState.hashCode()}")
val s = new StateImpl(sutState, neverExploredActions)
val s = new StateImpl(sutState)
states = states + (sutState -> s)
s
}
}

override def executeAction(from: State, a: Action, to: State): State = {
allExploredActions = allExploredActions + a
allNeverExploredActions = allNeverExploredActions - a
val old = actionExecutionTimes.get(a)
old match {
case Some(o) => actionExecutionTimes = actionExecutionTimes + (a -> (o + 1))
Expand All @@ -60,15 +52,12 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable {

override def getAllStates: Map[SutState, State] = states

override def getAllNeverExploredActions: Set[Action] = allNeverExploredActions

override def getAllExploredActions: Set[Action] = allExploredActions

override def getActionExecutionTimes: Map[Action, Int] = actionExecutionTimes

override def clear(): Unit = {
states = HashMap[SutState, State]()
allNeverExploredActions = HashSet[Action]()
allExploredActions = HashSet[Action]()
actionExecutionTimes = HashMap[Action, Int]()
}
Expand All @@ -85,7 +74,6 @@ class GuiStateMachineImpl extends GuiStateMachine with Serializable {
val readStateMachine = ois.readObject.asInstanceOf[GuiStateMachineImpl]
ois.close()
this.states = readStateMachine.states
this.allNeverExploredActions = readStateMachine.allNeverExploredActions
this.allExploredActions = readStateMachine.allExploredActions
this.actionExecutionTimes = readStateMachine.actionExecutionTimes
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import de.retest.ui.descriptors.SutState
import scala.collection.immutable.HashMap

@SerialVersionUID(1L)
class StateImpl(sutState: SutState, var neverExploredActions: Set[Action]) extends State with Serializable {
class StateImpl(sutState: SutState) extends State with Serializable {

/**
* TODO #4 Currently, there is no MultiMap trait for immutable maps in the Scala standard library.
Expand All @@ -16,7 +16,6 @@ class StateImpl(sutState: SutState, var neverExploredActions: Set[Action]) exten
var transitions = new HashMap[Action, ActionTransitions]

override def getSutState: SutState = sutState
override def getNeverExploredActions: Set[Action] = neverExploredActions
override def getTransitions: Map[Action, ActionTransitions] = transitions

private[api] override def addTransition(a: Action, to: State): Int = {
Expand All @@ -29,8 +28,6 @@ class StateImpl(sutState: SutState, var neverExploredActions: Set[Action]) exten

case None =>
transitions += (a -> ActionTransitions(Set(to), 1))
// In the legacy code this is done in `increaseTimesExecuted`.
neverExploredActions = neverExploredActions - a
1
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.io.File
import java.util.Arrays

import de.retest.guistatemachine.api.{AbstractApiSpec, Id}
import de.retest.surili.model.actions.{Action, NavigateToAction}
import de.retest.surili.model.actions.NavigateToAction
import de.retest.ui.descriptors.SutState

class GuiStateMachineApiImplSpec extends AbstractApiSpec {
Expand All @@ -21,7 +21,6 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec {
val fsm = stateMachine.get
fsm.getActionExecutionTimes.size shouldEqual 0
fsm.getAllExploredActions.size shouldEqual 0
fsm.getAllNeverExploredActions.size shouldEqual 0

sut.removeStateMachine(stateMachineId) shouldBe true
}
Expand All @@ -47,16 +46,14 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec {
val action1 = new NavigateToAction("http://wikipedia.org")

val initialSutState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC))
val initialNeverExploredActions = Set[Action](action0, action1)
val finalSutState = new SutState(Arrays.asList(rootElementC))
val finalNeverExploredActions = Set[Action](action0, action1)

// Create the whole state machine:
sut.clear()
stateMachineId = sut.createStateMachine()
val stateMachine = sut.getStateMachine(stateMachineId).get
val initialState = stateMachine.getState(initialSutState, initialNeverExploredActions)
val finalState = stateMachine.getState(finalSutState, finalNeverExploredActions)
val initialState = stateMachine.getState(initialSutState)
val finalState = stateMachine.getState(finalSutState)
stateMachine.executeAction(initialState, action0, finalState)

// Save all state machines:
Expand All @@ -75,7 +72,6 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec {
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.getAllStates.size shouldEqual 2
Expand All @@ -84,14 +80,12 @@ class GuiStateMachineApiImplSpec extends AbstractApiSpec {
loadedInitialState.getSutState shouldEqual initialSutState
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.head shouldEqual loadedFinalState
loadedFinalState.getSutState shouldEqual finalSutState
loadedFinalState.getTransitions.isEmpty shouldEqual true
loadedFinalState.getNeverExploredActions shouldEqual finalNeverExploredActions
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,67 +40,56 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {

"add two transitions to two new states for the same action and one transition to another state for another action" in {
val initialSutState = getSutState
val initial = sut.getState(initialSutState, getNeverExploredActions)
val initial = sut.getState(initialSutState)
sut.getAllExploredActions.size shouldEqual 0
sut.getAllNeverExploredActions.size shouldEqual 2
sut.getActionExecutionTimes.size shouldEqual 0

// execute action0 for the first time
val s0SutState = new SutState(Arrays.asList(rootElementA))
val s0 = sut.getState(s0SutState, getNeverExploredActions)
val s0 = sut.getState(s0SutState)
sut.executeAction(initial, action0, s0)
initial.getNeverExploredActions.size shouldEqual 1
initial.getTransitions.size 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(action0).isDefined shouldEqual true
sut.getActionExecutionTimes(action0) shouldEqual 1

// execute action0 for the second time
val s1SutState = new SutState(Arrays.asList(rootElementB))
val s1 = sut.getState(s1SutState, getNeverExploredActions)
val s1 = sut.getState(s1SutState)
sut.executeAction(initial, action0, s1)
initial.getNeverExploredActions.size shouldEqual 1
initial.getTransitions.size shouldEqual 1
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(action0).isDefined shouldEqual true
sut.getActionExecutionTimes(action0) shouldEqual 2

// execute action1 for the first time
val s2SutState = new SutState(Arrays.asList(rootElementC))
val s2 = sut.getState(s2SutState, getNeverExploredActions)
val s2 = sut.getState(s2SutState)
sut.executeAction(initial, action1, s2)
initial.getNeverExploredActions.size shouldEqual 0
initial.getTransitions.size shouldEqual 2
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(action1).isDefined shouldEqual true
sut.getActionExecutionTimes(action1) shouldEqual 1
}

"store a state for the second access" in {
val initialSutState = getSutState
val initialFromAccess0 = sut.getState(initialSutState, getNeverExploredActions)
val initialFromAccess1 = sut.getState(initialSutState, getNeverExploredActions)
val initialFromAccess0 = sut.getState(initialSutState)
val initialFromAccess1 = sut.getState(initialSutState)
initialFromAccess0 shouldEqual initialFromAccess1
}

"clear the state machine" in {
sut.clear()
sut.getAllNeverExploredActions.isEmpty shouldEqual true
sut.getAllExploredActions.isEmpty shouldEqual true
sut.actionExecutionTimes.isEmpty shouldEqual true
sut.getAllStates.isEmpty shouldEqual true
Expand All @@ -119,13 +108,11 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
val action1 = new NavigateToAction("http://wikipedia.org")

val initialSutState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC))
val initialNeverExploredActions = Set[Action](action0, action1)
val finalSutState = new SutState(Arrays.asList(rootElementC))
val finalNeverExploredActions = Set[Action](action0, action1)

// Create the whole state machine:
val initialState = sut.getState(initialSutState, initialNeverExploredActions)
val finalState = sut.getState(finalSutState, finalNeverExploredActions)
val initialState = sut.getState(initialSutState)
val finalState = sut.getState(finalSutState)
sut.executeAction(initialState, action0, finalState)

// Save the state machine:
Expand All @@ -140,7 +127,6 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {

// Verify the loaded state machine:
sut.getAllExploredActions.size shouldEqual 1
sut.getAllNeverExploredActions.size shouldEqual 1
sut.getActionExecutionTimes(action0) shouldEqual 1
sut.getActionExecutionTimes.contains(action1) shouldEqual false
sut.getAllStates.size shouldEqual 2
Expand All @@ -149,14 +135,12 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
loadedInitialState.getSutState shouldEqual initialSutState
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.head shouldEqual loadedFinalState
loadedFinalState.getSutState shouldEqual finalSutState
loadedFinalState.getTransitions.isEmpty shouldEqual true
loadedFinalState.getNeverExploredActions shouldEqual finalNeverExploredActions
}

"save GML " in {
Expand All @@ -167,22 +151,18 @@ class GuiStateMachineImplSpec extends AbstractApiSpec with BeforeAndAfterEach {
val action1 = new NavigateToAction("http://wikipedia.org")

val initialSutState = new SutState(Arrays.asList(rootElementA, rootElementB, rootElementC))
val initialNeverExploredActions = Set[Action](action0, action1)
val finalSutState = new SutState(Arrays.asList(rootElementC))
val finalNeverExploredActions = Set[Action](action0, action1)

// Create the whole state machine:
sut.clear()
val initialState = sut.getState(initialSutState, initialNeverExploredActions)
val finalState = sut.getState(finalSutState, finalNeverExploredActions)
val initialState = sut.getState(initialSutState)
val finalState = sut.getState(finalSutState)
sut.executeAction(initialState, action0, finalState)
sut.executeAction(initialState, action1, finalState)
sut.executeAction(finalState, action0, initialState)
sut.executeAction(finalState, action1, initialState)

initialState.getNeverExploredActions.size shouldEqual 0
initialState.getTransitions.size shouldEqual 2
finalState.getNeverExploredActions.size shouldEqual 0
finalState.getTransitions.size shouldEqual 2

val filePath = "./target/test_state_machine.gml"
Expand Down
Loading

0 comments on commit 7e308cc

Please sign in to comment.