Each test case is represented as a trace, a finite sequence of action and state pairs, the state corresponding to the results from those actions.
Formally, a trace
where
Each trace represents a refinement of specific system behaviors. In this context, refinement involves omitting certain non-essential states and actions (state transitions) while preserving only those states and actions of interest. The following figure illustrates how the refinement works.
When conducting deterministic testing, the tool identifies two types of inconsistencies between the implementation (referred to as the case) and the referenced model (derived from the specification).
The first is the inconsistent action.
Suppose the expected trace
However, the actual trace
Here, an inconsistent action occurs because,
During deterministic testing, the inconsistent actions can trigger a timeout.
The following figure shows the inconsistent action.
The second is the inconsistent state.
Suppose the expected trace
However, the actual trace
Here, there is an inconsistent state, because,
To detect such inconsistencies during deterministic testing, developers should add assertions in the source code. These assertions check for equality between the expected state from the testbed message and the actual current state of the program.
The following figure shows the inconsistent state.
When running deterministic testing, the message channel would be taken over by the deterministic player.
The figure below illustrates how the deterministic player reorders the actions based on predefined orders.
The physical system will align with the logical model, ensuring consistency. Our framework incorporates various macros to facilitate the implementation of anchor actions that verify the coherence between our source-level implementation and abstract-level design.