Test Point Testing in C/C++: Difference between revisions

From STRIDE Wiki
Jump to navigation Jump to search
m (Replace STRIDE with Stride)
 
(135 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Testpoints provide an easy-to-use framework for solving a class of common yet difficult unit testing problems:  
__NOTOC__
Test code to validate the [[Expectations]] of the Test Points often follows this basic pattern:


''How can I observe and verify activity that occurs in another thread?''
# Specify an expectation set consisting of expected (i.e. the test points that are expected to be hit) and optionally unexpected (i.e. the test points that are not expected to be hit) test points
# Register the expectation set with the Stride runtime
# Invoke the software under test (causing instrumentation points to be hit). This may not be necessary if the instrumented software under test is constantly running).
# Wait for the expectation set to be satisfied or a timeout to occur


A couple of common scenarios that become a lot more testable via testpoints include:
Here is an example:
* Verification of State machine operation
* Verification of communication drivers


<source lang="c">
#include <srtest.h>


== Instrumenting Target Threads ==
void tf_testpoint_wait(void)
Target threads are instrumented simply by placing lines of the following form into the source code:
{
<source lang='c'>
  /* specify expected set */
...
  srTestPointExpect_t expected[]= {
/* a testpoint with no payload */
        {"START"},
srTEST_POINT("first testpoint");
        {"ACTIVE"},
        {"IDLE"},
        {"END"},
        {0}};
 
  /* specify unexpected set */
  srTestPointUnexpect_t unexpected[]= {
        {"INVALID"},
        {0}};
 
  /* register the expectation set*/
  srWORD handle;
  srTestPointSetup(expected, unexpected, srTEST_POINT_EXPECT_UNORDERED, srTEST_CASE_DEFAULT, &handle);
 
  /* start your asynchronous operation */
  ...


srTEST_POINT_DATA("second testpoint", "string payload");
  /* wait for expectation set to be satisfied or a timeout to occur */
  srTestPointWait(handle, 1000);
}


srTEST_POINT_DATA1("third testpoint", "payload with format string %d", myVar);
#ifdef _SCL
#pragma scl_test_flist(“testfunc”, tf_testpoint_wait)
#endif
</source>
</source>


When this code is executed it broadcasts a message via the STRIDE runtime which is detected by the test (IM) thread if it is currently looking for testpoints (i.e. in a srTEST_POINT_WAIT()). We refer to this as a ''testpoint hit''.
== Instrumenting the Test Thread ==
The test thread is instrumented using these steps:


# Specify an expectation set (i.e. the testpoints that are expected to be hit)
== Reference ==
# Register the expectation set with the STRIDE runtime
# Wait for the expectation set to be satisfied or a timeout to occur


=== Expectation Set ===
An expectation set is specified with an [[Expectations#Expected_List|expected]] array of '''srTestPointExpect_t''' structures and a second optional [[Expectations#Unexpected_List|unexpected]] array of '''srTestPointUnexpect_t''' structures.


=== Specifying the Expectation Set ===
==== Expected Array ====
An expectation set is an array of '''srTestPointExpect_t''' structures. srTestPointExpect_t is typdef'd as follows:
srTestPointExpect_t is typedef'd as follows:


<source lang='c'>
<source lang='c'>
typedef struct
typedef struct
{
{
     /* the label value is considered the testpoint's identity */
     /* the label value is considered the test point's identity */
     const srCHAR * label;
     const srCHAR *         label;
     /* count specifies the number of times the testpoint is expected to be hit */  
     /* optional, count specifies the number of times the test point is expected to be hit */  
     srDWORD         count;
     srDWORD                 count;
     /* data specifies an optional string data payload */
     /* optional, predicate function to use for payload validation against user data */
     const srCHAR * data;
    srTestPointPredicate_t  predicate;
    /* optional, user data to validate the payload against */
     void *                 user;
} srTestPointExpect_t;
} srTestPointExpect_t;
</source>
</source>
==== Basic Example ====
 
A typical delcaration of an expectation set looks like this:
'''NOTES:'''
* The end of the array has to be marked by a srTestPointExpect_t set to all zero values
* The ''count'', ''predicate'' and ''user'' members may be omitted in the array declaration (they will be automatically set to 0 by the compiler)
* A ''count'' value of either 0 or 1 is interpreted as 1
* The ''count'' could be set as "0 or more" by using the [[Expectations#Special_Processing|special]] srTEST_POINT_ANY_COUNT symbolic constant 
* A ''predicate'' value 0 indicates that any associated data with a test point payload will be ignored.
* A ''user'' value 0 indicates that there is no user data associated with this test point
* The ''label'' could be specified to ''any test point'' within the current expected set of test points by using the [[Expectations#Special_Processing|special]] srTEST_POINT_ANY_IN_SET or srTEST_POINT_ANY_AT_ALL symbolic constants.
 
==== Unexpected Array ====
srTestPointUnexpect_t is typedef'd as follows:
 
<source lang='c'>
<source lang='c'>
    srTestPointExpect_t expected[]= {
typedef struct
        {"START"},
{
        {"ACTIVE"},
    /* the label value is considered the test point's identity */
        {"IDLE"},
    const srCHAR *          label;
        {"END"},
} srTestPointUnexpect_t;
        {0}};
</source>
</source>


This example specifies the expectation of one hit each of the testpoints "START", "ACTIVE", "IDLE", and "END".
'''NOTES:'''
* The end of the array has to be marked by a srTestPointUnexpect_t set to all zero values
* The ''label'' could be specified to ''everything else'' relative to the '''expected''' array by using the [[Expectations#Special_Processing|special]] srTEST_POINT_EVERYTHING_ELSE symbolic constant.  


A few things to note:
==== srTestPointPredicate_t ====
* The end of the array is marked by a srTestPointExpect_t set to all zero values
When defining the expectation set per entry a [[Expectations#State_Data_Validation|payload validation]] predicate function could be specified. The signature of it should match the following type:
* Structure members we omit in the array declaration are set to 0 by the compiler
* A count value of either 0 or 1 is interpreted as 1
* A data value 0 indicates that there is no payload data associated with this testpoint


=== Registering the Expectation Set ===
<source lang="c">
To register an expectation set, we call '''srTestPointExpect''' and receive a handle to identify the expectation  set in the next step.
extern "C" typedef srBYTE (*srTestPointPredicate_t)(const srTestPoint_t* ptTP, void* pvUser);
</source>


The registration looks like this:
{| border="1" cellspacing="0" cellpadding="10" style="align:left;" 
<source lang='c'>
| '''Parameters'''
srWORD handle;
| '''Type'''
srTestPointExpect(expected, &handle);
| '''Description'''
</source>
|-
| ptTP
| Input
| Pointer to the currently processed Test Point.
|-
| pvUser
| Input
| Pointer to opaque user data associated with an entry in the expectation set.
|}
<br>
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
| '''Return Value'''
| ''' Description'''
|-
| srBYTE
| srTRUE for valid, srFALSE for invalid, srIGNORE otherwise.
|}


Once the expectation set is registered, any testpoint hits are buffered and will be available to the subsequent '''srTEST_POINT_WAIT()'''. If the activity we will be obvserving/verifying needs to be started (e.g. a state machine gets kicked off) this should be done after '''srTestPointExpect()'''.
'''NOTE:'''
* As part of the standard Stride distribution there are three predefined function predicate helpers:
** srTestPointMemCmp - byte comparison
** srTestPointStrCmp - string case sensitive comparison
** srTestPointStrCaseCmp - string case insensitive comparison


== Waiting for the Expectation Set to be Satisfied ==
==== srTestPointSetup ====
The final step is to wait for the expectation to be satisfied. We do this using the '''srTEST_POINT_WAIT()''' macro as shown below.
The srTestPointSetup() routine is used to register an expectation set.


<source lang='c'>
<source lang="c">
srTEST_POINT_WAIT(handle, expect_flags, timeout);
srBOOL srTestPointSetup(srTestPointExpect_t* ptExpected,  
                        srTestPointUnexpect_t* ptUnexpected,
                        srBYTE yMode,
                        srTestCaseHandle_t tTestCase,  
                        srWORD* pwHandle);
</source>
</source>


The macro takes three arguments:
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
 
| '''Parameters'''  
{| class="prettytable"
| '''Type'''
|+ ''srTEST_POINT_WAIT() arguments''
| '''Description'''
| '''Argument''' || '''Description'''
|-
| ptExpected
| Input
| Pointer to an expectated array.
|-
| ptUnexpected
| Input
| Pointer to an unexpectated array. This is optional and could be set srNULL.
|-
| yMode
| Input
| Bitmask that specifies whether the expectated test points occur in [[Expectations#Sequencing_Properties|order and/or strict]]. Possible values are: <br/>
srTEST_POINT_EXPECT_ORDERED - the test points are expected to be hit exactly in the defined order <br/>
srTEST_POINT_EXPECT_UNORDERED - the test points could to be hit in any order <br/>
srTEST_POINT_EXPECT_STRICT - the test points are expected to be hit exactly as specified (no consecutive duplicate hits)<br/>
srTEST_POINT_EXPECT_NONSTRICT - other test points from the universe could to be hit in between <br/>
srTEST_POINT_EXPECT_CONTINUE - on successful expectation satisfaction continue processing until the wait timeout expires
|-
|-
| <tt>handle</tt>
| tTestCase 
| The handle returned from the call to '''srTestPointExpect()''' used to register the expectation set
| Input
| Handle to a test case. srTEST_CASE_DEFAULT can be used for the default test case.
|-
|-
| <tt>expect_flags</tt>
| pwHandle
| Value that customizes the expectation in terms of testpoint hit order and testpoint exclusivity (see below)
| Output
| Handle that represents the registered expectation set
|}
<br>
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
| '''Return Value'''
| ''' Description'''
|-
|-
| <tt>timeout</tt>
| srBOOL
| Timeout value in milliseconds; 0 means infinite timeout
| srTRUE on success, srFALSE otherwise.
|}
|}


The action the macro takes is as follows:
==== srTestPointWait ====
* The test thread (the thread macro is executed on) blocks until either the expectation set is satisfied or the timeout elpases. (Be careful with a timeout of 0, this can hang the test thread waiting for an expectation.)
The srTestPointWait() routine is used to wait for the expectation to be satisfied.
* All testpoints hit during the wait (both expected and unexpected) are added to the test report as testcase comments
* If an unexpected testpoint is encountered (either out of order or not in the expectation set), or the timeout period elapses before the expectation set is satisfied the current test immediately fails
* Once the wait is over (whether the expectation set has been satisfied or there has been a test failure), the current expectation set is automatically unregistered from the STRIDE runtime and the handle is released


=== Expect Flags ===
<source lang="c">
The following table shows the effect on expectations using different expect_flag values.
srBOOL srTestPointWait(srWORD wHandle,
                      srDWORD dwTimeout);
</source>


{| class="prettytable"
{| border="1" cellspacing="0" cellpadding="10" style="align:left;" 
|+ ''expect_flags values''
| '''Parameters'''  
| '''Expect Flags''' || align="center" | '''Unexpected Testpoint'''<br/>causes test failure || align="center" | '''Out of order Testpoint'''<br/>causes failure
| '''Type'''
| '''Description'''
|-
|-
| <tt>0</tt>
| wHandle
|  
| Input
|
| Handle to a registered expectation set.
|-
|-
| <tt>srTEST_POINT_WAIT_ORDER</tt>
| dwTimeout
|
| Input
| align="center" | X
| Timeout value in milliseconds; 0 means just check without waiting.
|}
<br>
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
| '''Return Value'''
| ''' Description'''
|-
| srBOOL
| srTRUE on success, srFALSE otherwise.
|}
 
'''NOTES:'''
* The test thread blocks until either the expectation set is satisfied, unless srTEST_POINT_EXPECT_CONTINUE is specified on setup, or the timeout elapses.
* All test points hit during the wait (both expected and unexpected) are added to the test report as testcase comments
* Once the wait is over (whether the expectation set has been satisfied or there has been a test failure), the current expectation set is automatically unregistered from the Stride runtime and the handle is released
* If you want to return immediately from a test case if expectation fails then make the check/wait call an argument to the ''srASSERT_TRUE()'' macro.
 
==== srTestPointCheck ====
The srTestPointCheck() routine is used to check for the expectation post routine completion. This is useful for verifying a set of expectations events that should have already transpired (thus are waiting to be processed).
 
<source lang="c">
srBOOL srTestPointCheck(srWORD wHandle);
</source>
 
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
| '''Parameters'''
| '''Type'''
| '''Description'''
|-
|-
| <tt>srTEST_POINT_WAIT_STRICT</tt>
| wHandle
| align="center" | X
| Input
|
| Handle to a registered expectation set.
|}
<br>
{| border="1" cellspacing="0" cellpadding="10" style="align:left;"
| '''Return Value'''
| ''' Description'''
|-
|-
| <tt><nowiki>srTEST_POINT_WAIT_ORDER | srTEST_POINT_WAIT_STRICT</nowiki></tt>
| srBOOL
| align="center" | X
| srTRUE on success, srFALSE otherwise.
| align="center" | X
|}
|}
'''NOTES:'''
* All test points hit before the check (both expected and unexpected) are added to the test report as testcase comments
* Once the check is done (whether the expectation set has been satisfied or there has been a test failure), the current expectation set is automatically unregistered from the Stride runtime and the handle is released
* If you want to return immediately from a test case if expectation fails then make the check/wait call an argument to the ''srASSERT_TRUE()'' macro.
=== C++ Facade Class ===
The ''srtest.h'' file provides a simple [http://en.wikipedia.org/wiki/Facade_pattern facade] class that wraps the test point APIs described above in a simple C++ class called '''srTestPointsHandler'''. If you are writing your unit tests in C++ (using Stride test classes), then this class is available for your use. The class implements the following methods, which correspond exactly to the C API equivalents:
; srTestPointsHandler : constructor. Takes four arguments which match exactly the first four parameters of the [[#srTestPointSetup|srTestPointSetup]] function.
; Wait : instance method that provides same functionality as [[#srTestPointWait|srTestPointWait]].
; Check : instance method that provides same functionality as [[#srTestPointCheck|srTestPointCheck]].

Latest revision as of 21:14, 8 July 2015

Test code to validate the Expectations of the Test Points often follows this basic pattern:

  1. Specify an expectation set consisting of expected (i.e. the test points that are expected to be hit) and optionally unexpected (i.e. the test points that are not expected to be hit) test points
  2. Register the expectation set with the Stride runtime
  3. Invoke the software under test (causing instrumentation points to be hit). This may not be necessary if the instrumented software under test is constantly running).
  4. Wait for the expectation set to be satisfied or a timeout to occur

Here is an example:

#include <srtest.h>

void tf_testpoint_wait(void)
{
  /* specify expected set */
  srTestPointExpect_t expected[]= {
        {"START"}, 
        {"ACTIVE"}, 
        {"IDLE"},
        {"END"}, 
        {0}};

  /* specify unexpected set */
  srTestPointUnexpect_t unexpected[]= {
        {"INVALID"}, 
        {0}};

  /* register the expectation set*/
  srWORD handle;
  srTestPointSetup(expected, unexpected, srTEST_POINT_EXPECT_UNORDERED, srTEST_CASE_DEFAULT, &handle);

  /* start your asynchronous operation */
  ...

  /* wait for expectation set to be satisfied or a timeout to occur */
  srTestPointWait(handle, 1000);
}

#ifdef _SCL
#pragma scl_test_flist(“testfunc”, tf_testpoint_wait)
#endif


Reference

Expectation Set

An expectation set is specified with an expected array of srTestPointExpect_t structures and a second optional unexpected array of srTestPointUnexpect_t structures.

Expected Array

srTestPointExpect_t is typedef'd as follows:

typedef struct
{
    /* the label value is considered the test point's identity */
    const srCHAR *          label;
    /* optional, count specifies the number of times the test point is expected to be hit */ 
    srDWORD                 count;
    /* optional, predicate function to use for payload validation against user data */ 
    srTestPointPredicate_t  predicate;
    /* optional, user data to validate the payload against */
    void *                  user;
} srTestPointExpect_t;

NOTES:

  • The end of the array has to be marked by a srTestPointExpect_t set to all zero values
  • The count, predicate and user members may be omitted in the array declaration (they will be automatically set to 0 by the compiler)
  • A count value of either 0 or 1 is interpreted as 1
  • The count could be set as "0 or more" by using the special srTEST_POINT_ANY_COUNT symbolic constant
  • A predicate value 0 indicates that any associated data with a test point payload will be ignored.
  • A user value 0 indicates that there is no user data associated with this test point
  • The label could be specified to any test point within the current expected set of test points by using the special srTEST_POINT_ANY_IN_SET or srTEST_POINT_ANY_AT_ALL symbolic constants.

Unexpected Array

srTestPointUnexpect_t is typedef'd as follows:

typedef struct
{
    /* the label value is considered the test point's identity */
    const srCHAR *          label;
} srTestPointUnexpect_t;

NOTES:

  • The end of the array has to be marked by a srTestPointUnexpect_t set to all zero values
  • The label could be specified to everything else relative to the expected array by using the special srTEST_POINT_EVERYTHING_ELSE symbolic constant.

srTestPointPredicate_t

When defining the expectation set per entry a payload validation predicate function could be specified. The signature of it should match the following type:

extern "C" typedef srBYTE (*srTestPointPredicate_t)(const srTestPoint_t* ptTP, void* pvUser);
Parameters Type Description
ptTP Input Pointer to the currently processed Test Point.
pvUser Input Pointer to opaque user data associated with an entry in the expectation set.


Return Value Description
srBYTE srTRUE for valid, srFALSE for invalid, srIGNORE otherwise.

NOTE:

  • As part of the standard Stride distribution there are three predefined function predicate helpers:
    • srTestPointMemCmp - byte comparison
    • srTestPointStrCmp - string case sensitive comparison
    • srTestPointStrCaseCmp - string case insensitive comparison

srTestPointSetup

The srTestPointSetup() routine is used to register an expectation set.

srBOOL srTestPointSetup(srTestPointExpect_t* ptExpected, 
                        srTestPointUnexpect_t* ptUnexpected, 
                        srBYTE yMode, 
                        srTestCaseHandle_t tTestCase, 
                        srWORD* pwHandle);
Parameters Type Description
ptExpected Input Pointer to an expectated array.
ptUnexpected Input Pointer to an unexpectated array. This is optional and could be set srNULL.
yMode Input Bitmask that specifies whether the expectated test points occur in order and/or strict. Possible values are:

srTEST_POINT_EXPECT_ORDERED - the test points are expected to be hit exactly in the defined order
srTEST_POINT_EXPECT_UNORDERED - the test points could to be hit in any order
srTEST_POINT_EXPECT_STRICT - the test points are expected to be hit exactly as specified (no consecutive duplicate hits)
srTEST_POINT_EXPECT_NONSTRICT - other test points from the universe could to be hit in between
srTEST_POINT_EXPECT_CONTINUE - on successful expectation satisfaction continue processing until the wait timeout expires

tTestCase Input Handle to a test case. srTEST_CASE_DEFAULT can be used for the default test case.
pwHandle Output Handle that represents the registered expectation set


Return Value Description
srBOOL srTRUE on success, srFALSE otherwise.

srTestPointWait

The srTestPointWait() routine is used to wait for the expectation to be satisfied.

srBOOL srTestPointWait(srWORD wHandle, 
                       srDWORD dwTimeout);
Parameters Type Description
wHandle Input Handle to a registered expectation set.
dwTimeout Input Timeout value in milliseconds; 0 means just check without waiting.


Return Value Description
srBOOL srTRUE on success, srFALSE otherwise.

NOTES:

  • The test thread blocks until either the expectation set is satisfied, unless srTEST_POINT_EXPECT_CONTINUE is specified on setup, or the timeout elapses.
  • All test points hit during the wait (both expected and unexpected) are added to the test report as testcase comments
  • Once the wait is over (whether the expectation set has been satisfied or there has been a test failure), the current expectation set is automatically unregistered from the Stride runtime and the handle is released
  • If you want to return immediately from a test case if expectation fails then make the check/wait call an argument to the srASSERT_TRUE() macro.

srTestPointCheck

The srTestPointCheck() routine is used to check for the expectation post routine completion. This is useful for verifying a set of expectations events that should have already transpired (thus are waiting to be processed).

srBOOL srTestPointCheck(srWORD wHandle);
Parameters Type Description
wHandle Input Handle to a registered expectation set.


Return Value Description
srBOOL srTRUE on success, srFALSE otherwise.

NOTES:

  • All test points hit before the check (both expected and unexpected) are added to the test report as testcase comments
  • Once the check is done (whether the expectation set has been satisfied or there has been a test failure), the current expectation set is automatically unregistered from the Stride runtime and the handle is released
  • If you want to return immediately from a test case if expectation fails then make the check/wait call an argument to the srASSERT_TRUE() macro.

C++ Facade Class

The srtest.h file provides a simple facade class that wraps the test point APIs described above in a simple C++ class called srTestPointsHandler. If you are writing your unit tests in C++ (using Stride test classes), then this class is available for your use. The class implements the following methods, which correspond exactly to the C API equivalents:

srTestPointsHandler
constructor. Takes four arguments which match exactly the first four parameters of the srTestPointSetup function.
Wait
instance method that provides same functionality as srTestPointWait.
Check
instance method that provides same functionality as srTestPointCheck.