Toggle Menu Visibility











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:
    1. StateID - unique identifier within the FSM

    2. StateAction - method which should be executed when the machine enters the state

    3. SuccessStateID - identifier of the state which the FSM should move to in case the StateAction succeeds (OnSuccess transition)

    4. FailureStateID - identifier of the state which the FSM should move to in case the StateAction fails (OnFailure transition)

    5. 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.