The Finite State Machine
The finite state machine tesing is based on the Finite State Automata theory.
In our case the machine has the following elements and properties:
- the FSM is built of states and transitions
- some the FSM states are final and other are starting. A state may be both starting and final
- each FSM state consists of the following properties:
-
StateID - unique identifier within the FSM
-
StateAction - method which should be executed when the machine enters the state
-
SuccessStateID - identifier of the state which the FSM should move to in case the StateAction succeeds (OnSuccess transition)
-
FailureStateID - identifier of the state which the FSM should move to in case the StateAction fails (OnFailure transition)
-
Data - passed to the StateAction (some plugin specific data)
The FSM is used to execute tests. The tests consist the following:
- CurrentStateID
- CurrentSubStateID
- Params table
- Result data (result code, details string, start and finish times)
The StateActions are simple methods, which take pointer to some test object, event mask and possibly pointer to some data. These methods return one of the following values:
- FSM_OK - the method finished successfully
- FSM_ERROR - error occurred while executing the method
- FSM_WAIT - this means neither error occurred, nor success was reached. Usually the method would return this value if its job could not be finished due to some reason (but didn't fail). In case the method returns this value, the test will stay in its current state till another event occurs, which could possibly finish the method execution.
List of all available at the moment State Action primitives
The FSM Algorithm
The Finite State Machine is initialized with pointers to TestDispather object, TestResultManager object and Params collection.
It runs the following procedure
PROCEDURE RunMachine
# endless loop
WHILE true DO
# check if some test timed out
FOR EACH pTestObj IN RunningTests DO
IF pTestObj->StartTime + pTestObj->Timeout > NOW() THEN
RunningTests.remove( pTestObj )
END IF
END FOR EACH
# fill the running test pool as much as possible
WHILE RunningTests.count < maxAsynchronousTests DO
pNewTestObject := pTestDispatcher->GetTest()
IF pNewTestObject = NIL THEN
BREAK
END IF
RunningTests.add( pNewTestObject );
END WHILE
WAIT_FOR_EVENTS_ON_SOCKETS( someTimeoutValue )
FOR EACH socket IN registeredSockets DO
IF eventMask := SOCKET_EVENT( socket ) THEN
pTestObject := FIND_TEST_BY_SOCKET( socket )
MoveMachine( pTestObject, eventMask )
END IF
END FOR EACH
END WHILE
END PROCEDURE
PROCEDURE MoveMachine( pTestObject, eventMask )
# execute the corresponding StateAction
rc := pTestObject->CurrentState->StateAction( pTestObject, eventMask )
# check if the state is final
IF IS_FINAL( pTestObject->CurrentState )
UNREGISTER_FOR_EVENTS( pTestObject->socket )
RETURN SUCCESS
END IF
IF rc = FSM_WAIT THEN
# the result was FSM_WAIT, so do nothing... successfully
RETURN SUCCESS
ELSE
# the result was either FSM_ERROR or FSM_OK
pTestObject->CurrentState = ( rc = FSM_OK ) ? pTestObject->SuccessStateID :
pTestObject->FailureStateID
pTestObject->CurrentSubState = 0
RETURN MoveMachine( pTestObject, NO_EVENTS )
END IF
END PROCEDURE
Use Case - The HTTP Plugin
The HTTP plugin has the following lifecycle for every test executed
The FSM is defined this way
runner.AddState( FSMState( 000, (fsm_method_t)&FSMTestRunner::InitSock, 001, 010, false ) );
runner.AddState( FSMState( 001, (fsm_method_t)&FSMTestRunner::Connect, 002, 010, false ) );
runner.AddState( FSMState( 002, (fsm_method_t)&FSMTestRunner::WriteSock, 003, 010, false ) );
runner.AddState( FSMState( 003, (fsm_method_t)&FSMTestRunner::ReadSock, 004, 010, false ) );
runner.AddState( FSMState( 004, (fsm_method_t)&FSMTestRunner::Close, 010, 010, false ) );
runner.AddState( FSMState( 010, (fsm_method_t)&FSMTestRunner::ReturnResult, 010, 010, true ) );
runner.SetInitial( 000 );
and simply by
runner.Run()
This table will build the following Finite State Machine:

Testing the FSM Scenario
-
Optimize the number of synchronously executed tests. Increase until timeouts begin to occur.
-
Test when the system refuses to initialize more sockets. This is very important number for the WatchCub as a whole.
-
Make sure there is test case which makes the machine walk all the transitions. Run at least 50 tests at once.