[PATCH 4/4] eng: Improve action requirements how-to
Sebastian Huber
sebastian.huber at embedded-brains.de
Wed Mar 17 17:34:26 UTC 2021
Update #3715.
---
eng/req/howto.rst | 418 +++++++++++++++++++++++++++++++++++++++--
eng/test-framework.rst | 2 +
2 files changed, 408 insertions(+), 12 deletions(-)
diff --git a/eng/req/howto.rst b/eng/req/howto.rst
index e7c35b3..ee942b8 100644
--- a/eng/req/howto.rst
+++ b/eng/req/howto.rst
@@ -414,12 +414,17 @@ requirements. Such requirements should be written in the following
**While** <pre-condition>, **while** ..., **when** <trigger>, the <system
name> shall <system response>.
-The list of <pre-condition> clauses are the pre-conditions in a particular
-state of the action requirement. The <trigger> is the action of the action
-requirement. The post-conditions in a particular state provide a list of the
-<system name> shall <system response> clauses. Each transition in the
+The list of *while* <pre-condition> clauses are the pre-conditions in a
+particular state of the action requirement. The <trigger> is the action of the
+action requirement. The post-conditions in a particular state provide a list
+of the <system name> shall <system response> clauses. Each entry in the
transition map is an event-driven requirement composed of the pre-condition
-states, the action, and the post-condition states defined by the map entry.
+states, the action, and the post-condition states.
+
+The trigger of the action requirement is defind by the link to an
+:ref:`SpecTypeInterfaceFunctionItemType` or an
+:ref:`SpecTypeInterfaceMacroItemType` item using the
+:ref:`SpecTypeInterfaceFunctionLinkRole`.
Use ``CamelCase`` for the pre-condition names, post-condition
names, and state names. The more conditions a directive has, the shorter
@@ -427,12 +432,401 @@ should be the names. The transition map may be documented as a table and more
conditions need more table columns. Use item attribute references in the
``text`` attributes. This allows context-sensitive substitutions.
-Link the action requirement item to an :ref:`SpecTypeInterfaceFunctionItemType`
-or an :ref:`SpecTypeInterfaceMacroItemType` item using the
-:ref:`SpecTypeInterfaceFunctionLinkRole`.
+Example
+^^^^^^^
+
+Lets have a look at an example of a action requirement. We would like to
+specify and validate the behaviour of the
+
+.. code-block:: c
+
+ rtems_status_code rtems_timer_create( rtems_name name, rtems_id *id );
+
+directive which is particularly simple. For a more complex example see the
+specification of :c:func:`rtems_signal_catch` or :c:func:`rtems_signal_send`.
+
+The trigger are calls to :c:func:`rtems_timer_create`. Firstly, we need the
+set of pre-conditions relevant to this directive. Good candidates are the
+directive parameters, this gives us the ``Name`` and ``Id`` conditions. A
+system condition is if an inactive timer object is available so that we can
+create a timer, this gives us the ``Free`` condition. Post-conditions are the
+return status of the directive, ``Status``, the validity of a unique object
+name, ``Name``, and the value of an object identifier variable, ``IdVar``.
+Each condition has a set of states, see the YAML data below for the details.
+The selected conditions and states yield the following transition map:
+
+.. table::
+ :class: longtable
+
+ ===== ========== ======= ===== ==== ======= ======= =====
+ Entry Descriptor Name Id Free Status Name IdVar
+ ===== ========== ======= ===== ==== ======= ======= =====
+ 0 0 Valid Valid Yes Ok Valid Set
+ 1 0 Valid Valid No TooMany Invalid Nop
+ 2 0 Valid Null Yes InvAddr Invalid Nop
+ 3 0 Valid Null No InvAddr Invalid Nop
+ 4 0 Invalid Valid Yes InvName Invalid Nop
+ 5 0 Invalid Valid No InvName Invalid Nop
+ 6 0 Invalid Null Yes InvName Invalid Nop
+ 7 0 Invalid Null No InvName Invalid Nop
+ ===== ========== ======= ===== ==== ======= ======= =====
+
+Not all transition maps are that small, the transition map of
+:c:func:`rtems_semaphore_create` has more than 4000 entries. We can construct
+requirements from the clauses of the entries. For example, the three
+requirements of entry 0 are:
+
+ While the ``name`` parameter is valid, while the ``id`` parameter
+ references an object of type rtems_id, while the system has at least one
+ inactive timer object available, when rtems_timer_create() is called, the
+ return status of rtems_timer_create() shall be RTEMS_SUCCESSFUL.
+
+ While the ``name`` parameter is valid, while the ``id`` parameter
+ references an object of type rtems_id, while the system has at least one
+ inactive timer object available, when rtems_timer_create() is called, the
+ unique object name shall identify the timer created by the
+ rtems_timer_create() call.
+
+ While the ``name`` parameter is valid, while the ``id`` parameter
+ references an object of type rtems_id, while the system has at least one
+ inactive timer object available, when rtems_timer_create() is called, the
+ value of the object referenced by the ``id`` parameter shall be set to the
+ object identifier of the created timer after the return of the
+ rtems_timer_create() call.
+
+Now we will have a look at the specification item line by line. The top-level
+attributes are normally in alphabetical order in an item file. For this
+presentation we use a structured order.
+
+.. code-block:: yaml
+
+ SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+ copyrights:
+ - Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
+ enabled-by: true
+ functional-type: action
+ rationale: null
+ references: []
+ requirement-type: functional
+
+The specification items need a bit of boilerplate to tell you what they are,
+who wrote them, and what their license is.
+
+.. code-block:: yaml
+
+ text: ${.:text-template}
+
+Each requirement item needs a ``text`` attribute. For the action requirements,
+we do not have a single requirement and there is no plain text. Several
+requirents are defined by the pre-conditions, the trigger, and the
+post-condistions.
+
+.. code-block:: yaml
+
+ pre-conditions:
+ - name: Name
+ states:
+ - name: Valid
+ test-code: |
+ ctx->name = NAME;
+ text: |
+ While the ${../if/create:/params[0]/name} parameter is valid.
+ - name: Invalid
+ test-code: |
+ ctx->name = 0;
+ text: |
+ While the ${../if/create:/params[0]/name} parameter is invalid.
+ test-epilogue: null
+ test-prologue: null
+ - name: Id
+ states:
+ - name: Valid
+ test-code: |
+ ctx->id = &ctx->id_value;
+ text: |
+ While the ${../if/create:/params[1]/name} parameter references an object
+ of type ${../../type/if/id:/name}.
+ - name: 'Null'
+ test-code: |
+ ctx->id = NULL;
+ text: |
+ While the ${../if/create:/params[1]/name} parameter is
+ ${/c/if/null:/name}.
+ test-epilogue: null
+ test-prologue: null
+ - name: Free
+ states:
+ - name: 'Yes'
+ test-code: |
+ /* Ensured by the test suite configuration */
+ text: |
+ While the system has at least one inactive timer object available.
+ - name: 'No'
+ test-code: |
+ ctx->seized_objects = T_seize_objects( Create, NULL );
+ text: |
+ While the system has no inactive timer object available.
+ test-epilogue: null
+ test-prologue: null
+
+This list defines the pre-conditions. Each pre-condition has a list of states
+and corresponding validation test code.
+
+.. code-block:: yaml
+
+ links:
+ - role: interface-function
+ uid: ../if/create
+ test-action: |
+ ctx->status = rtems_timer_create( ctx->name, ctx->id );
+
+The link to the :c:func:`rtems_timer_create` interface specification item with
+the ``interface-function`` link role defines the trigger. The ``test-action``
+defines the trigger code for the validation test. The validation test is
+defined in this item along with the specification.
+
+.. code-block:: yaml
+
+ post-conditions:
+ - name: Status
+ states:
+ - name: Ok
+ test-code: |
+ T_rsc_success( ctx->status );
+ text: |
+ The return status of ${../if/create:/name} shall be
+ ${../../status/if/successful:/name}.
+ - name: InvName
+ test-code: |
+ T_rsc( ctx->status, RTEMS_INVALID_NAME );
+ text: |
+ The return status of ${../if/create:/name} shall be
+ ${../../status/if/invalid-name:/name}.
+ - name: InvAddr
+ test-code: |
+ T_rsc( ctx->status, RTEMS_INVALID_ADDRESS );
+ text: |
+ The return status of ${../if/create:/name} shall be
+ ${../../status/if/invalid-address:/name}.
+ - name: TooMany
+ test-code: |
+ T_rsc( ctx->status, RTEMS_TOO_MANY );
+ text: |
+ The return status of ${../if/create:/name} shall be
+ ${../../status/if/too-many:/name}.
+ test-epilogue: null
+ test-prologue: null
+ - name: Name
+ states:
+ - name: Valid
+ test-code: |
+ id = 0;
+ sc = rtems_timer_ident( NAME, &id );
+ T_rsc_success( sc );
+ T_eq_u32( id, ctx->id_value );
+ text: |
+ The unique object name shall identify the timer created by the
+ ${../if/create:/name} call.
+ - name: Invalid
+ test-code: |
+ sc = rtems_timer_ident( NAME, &id );
+ T_rsc( sc, RTEMS_INVALID_NAME );
+ text: |
+ The unique object name shall not identify a timer.
+ test-epilogue: null
+ test-prologue: |
+ rtems_status_code sc;
+ rtems_id id;
+ - name: IdVar
+ states:
+ - name: Set
+ test-code: |
+ T_eq_ptr( ctx->id, &ctx->id_value );
+ T_ne_u32( ctx->id_value, INVALID_ID );
+ text: |
+ The value of the object referenced by the ${../if/create:/params[1]/name}
+ parameter shall be set to the object identifier of the created timer
+ after the return of the ${../if/create:/name} call.
+ - name: Nop
+ test-code: |
+ T_eq_u32( ctx->id_value, INVALID_ID );
+ text: |
+ Objects referenced by the ${../if/create:/params[1]/name} parameter in
+ past calls to ${../if/create:/name} shall not be accessed by the
+ ${../if/create:/name} call.
+ test-epilogue: null
+ test-prologue: null
+
+This list defines the post-conditions. Each post-condition has a list of
+states and corresponding validation test code.
+
+.. code-block:: yaml
+
+ skip-reasons: {}
+ transition-map:
+ - enabled-by: true
+ post-conditions:
+ Status:
+ - if:
+ pre-conditions:
+ Name: Invalid
+ then: InvName
+ - if:
+ pre-conditions:
+ Id: 'Null'
+ then: InvAddr
+ - if:
+ pre-conditions:
+ Free: 'No'
+ then: TooMany
+ - else: Ok
+ Name:
+ - if:
+ post-conditions:
+ Status: Ok
+ then: Valid
+ - else: Invalid
+ IdVar:
+ - if:
+ post-conditions:
+ Status: Ok
+ then: Set
+ - else: Nop
+ pre-conditions:
+ Name: all
+ Id: all
+ Free: all
+ type: requirement
-Pre-Conditions
-^^^^^^^^^^^^^^
+This list of transition descriptors defines the transition map. For the
+post-conditions, you can use expressions to ease the specification, see
+:ref:`SpecTypeActionRequirementTransitionPostConditionState`. The
+``skip-reasons`` can be used to skip entire entries in the transition map, see
+:ref:`SpecTypeActionRequirementSkipReasons`.
+
+.. code-block:: yaml
+
+ test-brief: null
+ test-description: null
+
+The item contains the validation test code. The validation test in general can
+be described by these two attributes.
+
+.. code-block:: yaml
+
+ test-target: testsuites/validation/tc-timer-create.c
+
+This is the target file for the generated validation test code.
+
+.. code-block:: yaml
+
+ test-includes:
+ - rtems.h
+ - string.h
+ test-local-includes: []
+
+You can specify a list of includes for the validation test.
+
+.. code-block:: yaml
+
+ test-header: null
+
+A test header may be used to create a parameterized validation test, see
+:ref:`SpecTypeTestHeader`. This is an advanced topic, see the specification of
+:c:func:`rtems_task_ident` for an example.
+
+.. code-block:: yaml
+
+ test-context-support: null
+ test-context:
+ - brief: |
+ This member is used by the T_seize_objects() and T_surrender_objects()
+ support functions.
+ description: null
+ member: |
+ void *seized_objects
+ - brief: |
+ This member may contain the object identifier returned by
+ rtems_timer_create().
+ description: null
+ member: |
+ rtems_id id_value
+ - brief: |
+ This member specifies the ${../if/create:/params[0]/name} parameter for the
+ action.
+ description: null
+ member: |
+ rtems_name name
+ - brief: |
+ This member specifies the ${../if/create:/params[1]/name} parameter for the
+ action.
+ description: null
+ member: |
+ rtems_id *id
+ - brief: |
+ This member contains the return status of the action.
+ description: null
+ member: |
+ rtems_status_code status
+
+You can specify a list of validation test context members which can be used to
+maintain the state of the validation test. The context is available through an
+implicit ``ctx`` variable in all code blocks except the support blocks. The
+context support code can be used to define test-specific types used by context
+members. Do not use global variables.
+
+.. code-block:: yaml
+
+ test-support: |
+ #define NAME rtems_build_name( 'T', 'E', 'S', 'T' )
+
+ #define INVALID_ID 0xffffffff
+
+ static rtems_status_code Create( void *arg, uint32_t *id )
+ {
+ return rtems_timer_create( rtems_build_name( 'S', 'I', 'Z', 'E' ), id );
+ }
+
+The support code block can be used to provide functions, data structures, and
+constants for the validation test.
+
+.. code-block:: yaml
+
+ test-prepare: null
+ test-cleanup: |
+ if ( ctx->id_value != INVALID_ID ) {
+ rtems_status_code sc;
+
+ sc = rtems_timer_delete( ctx->id_value );
+ T_rsc_success( sc );
+
+ ctx->id_value = INVALID_ID;
+ }
+
+ T_surrender_objects( &ctx->seized_objects, rtems_timer_delete );
+
+The validation test basically executes a couple of nested for loops to iterate
+over each pre-condition and each state of the pre-conditions. These two
+optional code blocks can be used to prepare the pre-condition state
+preparations and clean up after the post-condition checks in each loop
+iteration.
+
+.. code-block:: yaml
+
+ test-setup:
+ brief: null
+ code: |
+ memset( ctx, 0, sizeof( *ctx ) );
+ ctx->id_value = INVALID_ID;
+ description: null
+ test-stop: null
+ test-teardown: null
+
+These optional code blocks correspond to test fixture methods, see
+:ref:`RTEMSTestFrameworkFixture`.
+
+Pre-Condition Templates
+^^^^^^^^^^^^^^^^^^^^^^^
Specify all directive parameters as separate pre-conditions. Use the following
syntax for directive object identifier parameters:
@@ -507,8 +901,8 @@ Use the following syntax for other directive parameters:
test-epilogue: null
test-prologue: null
-Post-Conditions
-^^^^^^^^^^^^^^^
+Post-Condition Templates
+^^^^^^^^^^^^^^^^^^^^^^^^
Do not mix different things into one post-condition. If you write multiple
sentences to describe what happened, then think about splitting up the
diff --git a/eng/test-framework.rst b/eng/test-framework.rst
index 589caf7..bc6d300 100644
--- a/eng/test-framework.rst
+++ b/eng/test-framework.rst
@@ -131,6 +131,8 @@ two test steps (`N`) and one test failure (`F`). The test case execution
duration (`D`) was 0.001657 seconds. For test report details see:
:ref:`Test Reporting <RTEMSTestFrameworkTestReporting>`.
+.. _RTEMSTestFrameworkFixture:
+
Test Fixture
------------
--
2.26.2
More information about the devel
mailing list