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

Commit

Permalink
Improve GUI state machine API, remove experimental DSL and applicatio…
Browse files Browse the repository at this point in the history
…n REST support #1 #4

- Store descriptors per state and define getDescriptors in the trait State.
- Fix comment about legacy code.
- Add State.addTransition.
- Remove all the DSL code which was experimental.
- Remove all the REST applications/test suites code which was experimental.
- Move model into rest.model.
- Adapt unit tests.
  • Loading branch information
tdauth committed Nov 12, 2018
1 parent 08014c6 commit f444399
Show file tree
Hide file tree
Showing 52 changed files with 291 additions and 682 deletions.
39 changes: 3 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
REST service for the creation and modification of nondeterministic finite automaton for the automatic generation of GUI tests with the help of a genetic algorithm.
The service hides the actual implementation and defines a fixed interface for calls.
Therefore, calling systems do not depend on the concrete implementation and it can be mocked easily for tests.
Basically, it does only provide only the two calls `getState` and `executeAction`.

## Automatic Build with TravisCI
[![Build Status](https://travis-ci.org/retest/gui-state-machine-api.svg?branch=master)](https://travis-ci.org/retest/gui-state-machine-api)
Expand Down Expand Up @@ -56,29 +57,6 @@ A state is defined by the set of all visible and interactable windows together w
## Scala API for GUI State Machines
The package [api](./src/main/scala/de/retest/guistatemachine/api/) contains all types and methods for getting and modifying the GUI state machine.

## DSL
There is a DSL to construct an NFA with GUI actions manually.
The package [dsl](./src/main/scala/de/retest/guistatemachine/dsl/).

The following example shows how to construct an NFA in Scala:
```scala
case object Start extends InitialState
case object S0 extends State
case object S1 extends State
case object End extends FinalState
case object EnterText extends Action
case object PressExitButton extends Action

StateMachines {
StateMachine {
Start - EnterText - S0
Start - EnterText - S1
S0 - PressExitButton - End
S1 - PressExitButton - End
}
}
```

## REST API
Some suggestions how the REST API for the state machine could look like:
* `/state-machines` GET queries all existing state machines.
Expand All @@ -90,19 +68,8 @@ Some suggestions how the REST API for the state machine could look like:
* `/state-machine/<long>/state/<long>/transition/<long>` GET queries a specific transition of a specific state.
* `/state-machine/<long>/execute` POST executes the passed action from the passed state which might lead to a new state and adds a transition to the state machine. The action must be part of all actions?

Some suggestions on how the test representation REST API could look like (not necessarily required):

* `/applications` GET queries all existing GUI applications.
* `/create-application` POST creates a new GUI application.
* `/application/<long>` GET queries an existing GUI application.
* `/application/<long>` DELETE deletes an existing GUI application and all of its test suites etc.
* `/application/<long>/test-suites` GET queries all test suites for an existing GUI application.
* `/application/<long>/create-test-suite` POST creates a new test suite for an existing GUI application.
* `/application/<long>/test-suite/<long>` GET queries an existing test suite for an existing GUI application.
* `/application/<long>/test-suite/<long>` DELETE deletes an existing test suite for an existing GUI application.

## NFA Frameworks
This list contains frameworks for Scala which support the representation of an NFA:
## Possible NFA Frameworks
This list contains frameworks for Scala which support the representation of an NFA and could be used as backend to construct the state machine:
* Akka FSM (FSM for actors): <https://doc.akka.io/docs/akka/current/fsm.html>
* Neo4J: <https://neo4j.com/>
* Gremlin-Scala: <https://github.com/mpollmeier/gremlin-scala>
6 changes: 5 additions & 1 deletion src/main/scala/de/retest/guistatemachine/api/Action.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ package de.retest.guistatemachine.api
/**
* Interaction from the user with the GUI.
*/
case class Action(a : org.openqa.selenium.interactions.Action)
case class Action(a: org.openqa.selenium.interactions.Action) {

// TODO #5 Convert abstract representation of actions into string.
override def toString: String = "Selenium Action"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import de.retest.ui.descriptors.RootElement
/**
* Set of root elements which identifies a state.
*/
case class Descriptors(rootElements: Set[RootElement])
case class Descriptors(rootElements: Set[RootElement])
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ trait GuiStateMachine {
* @return The current state which the transition of a leads to.
*/
def executeAction(from: State, a: Action, descriptors: Descriptors, neverExploredActions: Set[Action]): State
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.retest.guistatemachine.api

trait GuiStateMachineApi {

/**
* Creates a new [[GuiStateMachine]].
* @return The new GUI state machine.
*/
def createStateMachine: GuiStateMachine

/**
* Removes a persisted [[GuiStateMachine]].
* @param stateMachine The persisted GUI state machine.
* @return True if it has been persisted before and is no remove. Otherwise, false.
*/
def removeStateMachine(stateMachine: GuiStateMachine): Boolean
}
29 changes: 28 additions & 1 deletion src/main/scala/de/retest/guistatemachine/api/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,38 @@ package de.retest.guistatemachine.api
* It consists of actions which have not been explored yet and transitions which build up the state machine.
*/
trait State {
def getDescriptors: Descriptors

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.
*/
def getTransitions: Map[Action, Set[State]]
}

/**
* 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.
*/
private[api] def addTransition(a: Action, to: State): Unit

/**
* 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
* for each root element.
*/
override def equals(obj: Any): Boolean = {
if (obj.isInstanceOf[State]) {
val other = obj.asInstanceOf[State]
this.getDescriptors == other.getDescriptors
} else {
super.equals(obj)
}
}

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

override def toString: String = s"descriptors=${getDescriptors},neverExploredActions=${getNeverExploredActions},transitions=${getTransitions}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package de.retest.guistatemachine.api.impl

import de.retest.guistatemachine.api.GuiStateMachineApi
import de.retest.guistatemachine.api.GuiStateMachine

import scala.collection.mutable.HashSet

object GuiStateMachineApiImpl extends GuiStateMachineApi {
// TODO #4 Use Persistence instead of a custom set?
val stateMachines = new HashSet[GuiStateMachine]

override def createStateMachine: GuiStateMachine = {
val r = new GuiStateMachineImpl
stateMachines += r
r
}

override def removeStateMachine(stateMachine: GuiStateMachine): Boolean = stateMachines.remove(stateMachine)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import de.retest.guistatemachine.api.{Action, Descriptors, GuiStateMachine, Stat

import scala.collection.mutable.HashMap

object GuiStateMachineImpl extends GuiStateMachine {
class GuiStateMachineImpl extends GuiStateMachine {
val states = new HashMap[Descriptors, State]

override def getState(descriptors: Descriptors, neverExploredActions: Set[Action]): State = {
Expand All @@ -19,7 +19,7 @@ object GuiStateMachineImpl extends GuiStateMachine {

override def executeAction(from: State, a: Action, descriptors: Descriptors, neverExploredActions: Set[Action]): State = {
val to = getState(descriptors, neverExploredActions)
from.asInstanceOf[StateImpl].addTransition(a, to)
from.addTransition(a, to)
to
}
}
}
25 changes: 5 additions & 20 deletions src/main/scala/de/retest/guistatemachine/api/impl/StateImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,21 @@ import scala.collection.immutable.{HashMap, HashSet}
class StateImpl(val descriptors: Descriptors, var neverExploredActions: Set[Action]) extends State {

/**
* TODO #4 Currently, there is no MultiMap trait for immutable maps.
* TODO #4 Currently, there is no MultiMap trait for immutable maps in the Scala standard library.
*/
var transitions = new HashMap[Action, Set[State]]

override def getDescriptors: Descriptors = descriptors
override def getNeverExploredActions: Set[Action] = neverExploredActions
override def getTransitions: Map[Action, Set[State]] = transitions

def addTransition(a: Action, to: State): Unit = {
private[api] override def addTransition(a: Action, to: State): Unit = {
if (!transitions.contains(a)) {
transitions = transitions + (a -> HashSet(to))
// TODO #4 This is not done in the legacy code:
// In the legacy code this is done in `increaseTimesExecuted`.
neverExploredActions = neverExploredActions - a
} else {
transitions = transitions + (a -> (transitions(a) + to))
}
}

/**
* 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
* for each root element.
*/
override def equals(obj: Any): Boolean = {
if (obj.isInstanceOf[StateImpl]) {
val other = obj.asInstanceOf[StateImpl]
this.descriptors == other.descriptors
} else {
super.equals(obj)
}
}

override def hashCode(): Int = this.descriptors.hashCode()
}
}
3 changes: 0 additions & 3 deletions src/main/scala/de/retest/guistatemachine/dsl/Action.scala

This file was deleted.

6 changes: 0 additions & 6 deletions src/main/scala/de/retest/guistatemachine/dsl/FinalState.scala

This file was deleted.

This file was deleted.

30 changes: 0 additions & 30 deletions src/main/scala/de/retest/guistatemachine/dsl/State.scala

This file was deleted.

50 changes: 0 additions & 50 deletions src/main/scala/de/retest/guistatemachine/dsl/StateMachine.scala

This file was deleted.

25 changes: 0 additions & 25 deletions src/main/scala/de/retest/guistatemachine/dsl/StateMachines.scala

This file was deleted.

15 changes: 0 additions & 15 deletions src/main/scala/de/retest/guistatemachine/dsl/Transition.scala

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions src/main/scala/de/retest/guistatemachine/model/Action.scala

This file was deleted.

3 changes: 0 additions & 3 deletions src/main/scala/de/retest/guistatemachine/model/Actions.scala

This file was deleted.

Loading

0 comments on commit f444399

Please sign in to comment.